660

私が次のものを持っていると仮定します:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

次の結果配列を取得するように、すべての異なる年齢の配列を取得できるようにする最良の方法は何ですか?

[17, 35]

「年齢」の値をチェックする各配列を繰り返し処理し、別の配列に対してその存在をチェックし、そうでない場合は追加する必要がないように、代わりにデータまたはより良い方法を構造化する方法はありますか?

反復せずに個別の年齢を引き出すことができる方法があれば...

改善したい現在の非効率的な方法...「配列」がオブジェクトの配列ではなく、一意のキー(つまり「1,2,3」)を持つオブジェクトの「マップ」であることを意味する場合大丈夫です。最もパフォーマンス効率の高い方法を探しているだけです。

以下は私が現在行っている方法ですが、私にとっては、反復は機能していても効率が悪いように見えます...

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)
4

58 に答える 58

1047

ES6/ES2015 以降を使用している場合は、次の方法で実行できます。

const data = [
  { group: 'A', name: 'SD' }, 
  { group: 'B', name: 'FI' }, 
  { group: 'A', name: 'MM' },
  { group: 'B', name: 'CO'}
];
const unique = [...new Set(data.map(item => item.group))]; // [ 'A', 'B']

これを行う方法の例を次に示します。

于 2016-01-29T19:31:14.317 に答える
155

これが PHP の場合、キーを使用して配列を作成array_keysし、最後に取得しますが、JS にはそのような贅沢はありません。代わりに、これを試してください:

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}
于 2013-02-28T01:36:10.203 に答える
152

ES6 機能を使用すると、次のようなことができます。

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
于 2015-11-04T01:37:23.127 に答える
148

このような辞書アプローチを使用できます。基本的に、区別したい値を「ディクショナリ」のキーとして割り当てます(ここでは、ディクショナリモードを回避するために配列をオブジェクトとして使用します)。キーが存在しなかった場合は、その値を個別に追加します。

これが実際のデモです:

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
var unique = [];
var distinct = [];
for( let i = 0; i < array.length; i++ ){
  if( !unique[array[i].age]){
    distinct.push(array[i].age);
    unique[array[i].age] = 1;
  }
}
var d = document.getElementById("d");
d.innerHTML = "" + distinct;
<div id="d"></div>

これはO(n)になります。ここで、nは配列内のオブジェクトの数、mは一意の値の数です。各値を少なくとも1回検査する必要があるため、O(n)よりも高速な方法はありません。

これの以前のバージョンは、オブジェクトとfor inを使用していました。これらは本質的にマイナーであり、それ以降、上記でマイナーに更新されています。ただし、元のjsperfの2つのバージョン間でパフォーマンスが向上したように見える理由は、データサンプルサイズが非常に小さいためです。したがって、以前のバージョンでの主な比較は、内部マップとフィルターの使用と辞書モードのルックアップの違いを調べることでした。

上記のコードを更新しましたが、jsperfも更新して、3ではなく1000個のオブジェクトを調べました。3は、関連するパフォーマンスの落とし穴の多くを見落としていました(廃止されたjsperf)。

パフォーマンス

https://jsperf.com/filter-vs-dictionary-more-data実行したとき、この辞書は96%高速でした。

フィルタと辞書

于 2013-02-28T01:37:15.447 に答える
69

重複をマッピングして削除するだけです。

var ages = array.map(function(obj) { return obj.age; });
ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });

console.log(ages); //=> [17, 35]

編集:エイト!パフォーマンスの点で最も効率的な方法ではありませんが、最も単純で最も読みやすいIMOです。マイクロ最適化に本当に関心がある場合、または大量のデータがある場合は、通常のforループの方が「効率的」です。

于 2013-02-28T01:40:26.913 に答える
49
var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];

// or with lodash

var unique = _.uniq(_.map(array, 'age'));

ES6 の例

const data = [
  { name: "Joe", age: 17}, 
  { name: "Bob", age: 17}, 
  { name: "Carl", age: 35}
];

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications, but it's still a set
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or use Array.from to transform a set into an array
const unique2 = Array.from(s); // [17, 35]
于 2018-12-16T16:18:26.380 に答える
33

reduce()すでに多くの有効な回答がありますが、クリーンでシンプルなメソッドのみを使用するものを追加したかったの です。

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}

次のように使用します。

var array = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]
于 2018-08-02T13:39:58.867 に答える
22

forEach@travis-j の回答のバージョン (最新のブラウザーと Node JS の世界で役立ちます):

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

Chrome v29.0.1547 で 34% 高速化: http://jsperf.com/filter-versus-dictionary/3

そして、マッパー関数を使用する一般的なソリューション(直接マップよりも少し遅いですが、それは予想されます):

function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]
于 2013-09-20T18:28:17.980 に答える
17

私はデフォルトですべての新しいプロジェクトにアンダースコアを付け始めたので、これらの小さなデータ管理の問題について考える必要はありません。

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

を生成し[17, 35]ます。

于 2013-02-28T01:45:51.690 に答える
13

これを解決する別の方法を次に示します。

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

このソリューションが他のソリューションと比べてどれだけ速いかはわかりませんが、すっきりとした見た目が気に入っています。;-)


編集:さて、上記はここで最も遅い解決策のようです。

ここでパフォーマンス テスト ケースを作成しました: http://jsperf.com/distinct-values-from-array

年齢 (整数) をテストする代わりに、名前 (文字列) を比較することにしました。

方法 1 (TS のソリューション) は非常に高速です。興味深いことに、メソッド 7 は他のすべてのソリューションよりも優れています。ここでは、.indexOf() を取り除き、それを「手動で」実装して、ループ関数呼び出しを回避しました。

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

Safari と Firefox を使用した場合のパフォーマンスの違いは驚くべきものであり、最適化に関しては Chrome が最も優れているようです。

上記のスニペットが他のスニペットに比べて非常に高速である理由は正確にはわかりません。おそらく、私よりも賢い誰かが答えを持っているでしょう。;-)

于 2015-02-13T17:47:46.947 に答える
11

ロダッシュの使用

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]
于 2015-01-21T14:01:59.007 に答える
6
function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) == "undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}
于 2015-12-30T13:35:13.610 に答える
5

アンダースコア.js _.uniq(_.pluck(array,"age"))

于 2016-05-02T17:24:06.173 に答える
5

オブジェクトの一意のリストを返したい場合。ここに別の選択肢があります:

const unique = (arr, encoder=JSON.stringify, decoder=JSON.parse) =>
  [...new Set(arr.map(item => encoder(item)))].map(item => decoder(item));

これは次のようになります。

unique([{"name": "john"}, {"name": "sarah"}, {"name": "john"}])

の中へ

[{"name": "john"}, {"name": "sarah"}]

ここでの秘訣は、最初に を使用して項目を文字列にエンコードしJSON.stringify、次にそれを Set に変換し (文字列のリストを一意にする)、次に を使用して元のオブジェクトに変換し直すことJSON.parseです。

于 2020-07-07T13:54:05.120 に答える
4

これを見つけただけで、便利だと思いました

_.map(_.indexBy(records, '_id'), function(obj){return obj})

再びアンダースコアを使用するため、このようなオブジェクトがある場合

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

一意のオブジェクトのみが提供されます。

ここで起こることは、indexByこのようなマップを返すことです

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

マップであるという理由だけで、すべてのキーは一意です。

次に、このリストを配列にマッピングします。

個別の値のみが必要な場合

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

は文字列として返されることkeyに注意してください。代わりに整数が必要な場合は、

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})
于 2014-07-23T12:10:31.523 に答える
3

Array.prototype.includes を持っているか、それをポリフィルしたい場合、これは機能します:

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });
于 2015-05-20T23:13:22.433 に答える
3

私のように、速度を犠牲にすることなくより「機能的」なものを好む場合は、この例では、reduce クロージャー内にラップされた高速な辞書検索を使用します。

var array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

このテストによると、私のソリューションは提案された回答の 2 倍高速です

于 2016-01-31T06:54:21.317 に答える
2

const array = [{
    "name": "Joe",
    "age": 17
  },
  {
    "name": "Bob",
    "age": 17
  },
  {
    "name": "Carl",
    "age": 35
  }
]

const uniqueArrayByProperty = (array, callback) => {
  return array.reduce((prev, item) => {
    const v = callback(item);    
    if (!prev.includes(v)) prev.push(v)          
    return prev
  }, [])    
}

console.log(uniqueArrayByProperty(array, it => it.age));

于 2021-05-17T14:09:07.550 に答える
2

優れたパフォーマンスを備えたシンプルなワンライナー。私のテストでは、ES6 ソリューションよりも 6% 高速です。

var ages = array.map(function(o){return o.age}).filter(function(v,i,a) {
    return a.indexOf(v)===i
});
于 2019-04-30T07:21:41.607 に答える
1

Kotlinのような一般的なケースのために、TypeScriptで自分自身を書きましたArray.distinctBy {}...

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

Uもちろん、ハッシュ可能な場所はどこですか。オブジェクトの場合、 https://www.npmjs.com/package/es6-json-stable-stringifyが必要になる場合があります

于 2019-08-27T10:40:10.603 に答える
0
unique(obj, prop) {
    let result = [];
    let seen = new Set();

    Object.keys(obj)
        .forEach((key) => {
            let value = obj[key];

            let test = !prop
                ? value
                : value[prop];

            !seen.has(test)
                && seen.add(test)
                && result.push(value);
        });

    return result;
}
于 2017-10-10T22:48:56.583 に答える
0

let mobilePhones = [{id: 1, brand: "B1"}, {id: 2, brand: "B2"}, {id: 3, brand: "B1"}, {id: 4, brand: "B1"}, {id: 5, brand: "B2"}, {id: 6, brand: "B3"}]
 let allBrandsArr = mobilePhones .map(row=>{
        return  row.brand;
      });
let uniqueBrands =   allBrandsArr.filter((item, index, arry) => (arry.indexOf(item) === index));
console.log('uniqueBrands   ', uniqueBrands );

于 2021-07-20T06:32:56.383 に答える
-2

これは古くて比較的よく答えられた質問であり、私が提供している答えは完全なオブジェクトを取り戻すことを知っています(この投稿の多くのコメントで提案されています)。「粘着性がある」かもしれませんが、読みやすさの点では、他の多くのソリューションよりも(効率は劣りますが)きれいに見えます。

これにより、配列内の完全なオブジェクトの一意の配列が返されます。

let productIds = data.map(d => { 
   return JSON.stringify({ 
      id    : d.sku.product.productId,
      name  : d.sku.product.name,
      price : `${d.sku.product.price.currency} ${(d.sku.product.price.gross / d.sku.product.price.divisor).toFixed(2)}`
   })
})
productIds = [ ...new Set(productIds)].map(d => JSON.parse(d))```
于 2019-07-14T08:40:06.383 に答える
-5

d3.js v3を使用:

  ages = d3.set(
    array.map(function (d) { return d.age; })
  ).values();
于 2017-07-26T11:52:19.210 に答える