JavaScriptでオブジェクトの配列から個別の値を取得する方法は?


384

私が以下を持っていると仮定します:

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

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

[17, 35]

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

何らかの方法があったら、反復せずに異なる年齢を引き出すことができます...

私が改善したい現在の非効率的な方法...それは、オブジェクトの配列である「配列」の代わりに、いくつかの一意のキーを持つオブジェクトの「マップ」(つまり、「1、2、3」)であることを意味します大丈夫。Imは、最も効率的な方法を探しています。

以下は私が現在それをしている方法ですが、私にとって、反復はそれが機能しているにもかかわらず、効率のために不器用であるように見えます...

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

34
反復は「効率の悪さ」ではなく、「反復せずに」すべての要素に何もすることはできません。さまざまな機能的な方法を使用できますが、最終的には、あるレベルの何かがアイテムを反復処理する必要があります。
イーブイ2013

// 100%実行コードconst listOfTags = [{id:1、label: "Hello"、color: "red"、sorting:0}、{id:2、label: "World"、color: "green"、sorting :1}、{id:3、label: "Hello"、color: "blue"、sorting:4}、{id:4、label: "Sunshine"、color: "yellow"、sorting:5}、{id :5、label: "Hello"、color: "red"、sorting:6}]、keys = ['label'、 'color']、filtered = listOfTags.filter((s => o =>(k => !s.has(k)&& s.add(k))(keys.map(k => o [k])。join( '|')))(新しいセット)); console.log(filtered);
Sandeep Mishra

1
賞金は素晴らしいですが、与えられたデータと回答のある質問は、ここですでに回答されています:stackoverflow.com/questions/53542882/…。賞金の目的は何ですか?2つ以上のキーでこの特定の問題に答えるべきですか?
Nina Scholz、

Setオブジェクトとmapは無駄です。この仕事は単純な.reduce()段階を踏みます。
Redu

回答:


126

これが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);
}

15
いいえ、array_uniqueここで尋ねられる年齢だけでなく、アイテム全体を比較するためです。
Niet the Dark Absol 2013

6
flags = {}はより良いと思いますflags = []
zhuguowei

3
@zhuguoweiおそらく、まばらな配列には何も問題はありませんが、それもage比較的小さな整数(確かに120未満)であると想定しました
Niet the Dark Absol 2017年

同じ年齢の人の総数を出力するにはどうしたらいいでしょうか?
user1788736

606

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

const unique = [...new Set(array.map(item => item.age))];

ここでそれを行う方法についての例があります。


11
エラーが発生しました:TypeError: (intermediate value).slice is not a function
GithubのAngJobs

7
@Thomas ...はスプレッド演算子を意味します。この場合、それは新しいセットを作成してリストに広げることを意味します。詳細については、developer.mozilla.org
en

10
typescriptの場合、Array.from()でラップされた新しいセットを使用する必要があります...以下でその答えを提供します。@AngJobs
クリスチャンマシュー

4
オブジェクトの複数のキーに基づいて一意のオブジェクトを見つけることは可能ですか?
Jefree Sujit 2017年

14
このソリューションは、@ Russell Veaによって数か月前にほとんど逐語的に与えられました。既存の回答を確認しましたか?しかし、266票以上の投票は、他の誰もチェックしなかったことを示しています。
Dan Dascalescu、2018

165

ES6の使用

let array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]

2
インデックスで値を取得したい場合、どのように取得できますか?例:{"name": "Bob"、 "age": "17"}、{"name": "Bob"、 "age ":" 17 "}
アブドラアルマムン2018

10
そのため、スクロールし続ける必要があります
アレハンドロバスティダス

5
このように、あなたは.MAP最初の代わりに呼び出しの.filterにはマップを使用することができ@AbdullahAlMamun:array.filter((value, index, self) => self.map(x => x.age).indexOf(value.age) == index)
レオ

1
ps:.filter内の.mapは遅くなる可能性があるため、大きな配列には注意してください
Leo

2
@IvanNosovスニペットは非常に優れていますが、ドキュメントへのポインターを使用してドキュメントをフィルター処理する方法を説明する数行の説明により、初心者にとって完璧かつ明確になります
azakgaim

128

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

これは実際のデモです:

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)より速い方法はありません。

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

私が述べたように、しかし、私はまた、(関与パフォーマンスの落とし穴の多くの見落とし1000個のオブジェクトを通しての代わりに、3 3外観にjsperfを更新した、上記のコードを更新した時代遅れjsperfを)。

パフォーマンス

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

フィルター対辞書


4
高速で、追加のプラグインは必要ありません。本当に涼しい
mihai

2
いかがですかif( typeof(unique[array[i].age]) == "undefined"){ distinct.push(array[i].age); unique[array[i].age] = 0; }
Timeless

7
うわー、パフォーマンスは実際にマップオプションに変更されました。そのjsperfリンクをクリックしてください!
AT

1
@ 98percentmonkey-オブジェクトは、「辞書アプローチ」を容易にするために使用されました。オブジェクト内では、追跡されている各オカレンスがキー(またはビン)として追加されます。このようにして、オカレンスに遭遇したときに、キー(またはビン)が存在するかどうかを確認できます。それが存在しない場合、我々は、発生が一意でないことを知っている-それが存在しない場合、我々は、発生が一意であることを知っているし、それを追加します。
トラヴィスJ

1
@ 98percentmonkey-オブジェクトを使用する理由は、プロパティ名を1回チェックするためルックアップがO(1)であるのに対し、配列は存在をチェックするためにすべての値を調べる必要があるためO(n)です。プロセスの最後に、オブジェクトのキー(ビン)のセットは、一意のオカレンスのセットを表します。
Travis J

91

これは、2017年8月25日の時点でTypescriptのES6を介した新しいセットを使用してこれを解決する方法です

Array.from(new Set(yourArray.map((item: any) => item.id)))

3
これは助けになり、感謝の前に得ていました。タイプ 'Set'は配列タイプではありません
robert king

1
これは素晴らしい答えです。私がこれまでスクロールダウンするまで、私は答えを書こうとしました:Object.keys(values.reduce <any>((x、y)=> {x [y] = null; return x;}、{} )); しかし、この答えはより簡潔で、文字列だけでなくすべてのデータ型に適用されます。
jgosar

1
この答えは壮観です!
lukeocodes

79

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

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];

6
このアプローチはプリミティブ型でのみ機能します(年齢は数値の配列であるため、この場合に当てはまります)が、オブジェクトでは失敗します。
k0pernikus

2つのキーを持つオブジェクトで機能させる方法はありますか?
cegprakash

1
何かconst uniqueObjects = [ ...new Set( array.map( obj => obj.age) ) ].map( age=> { return array.find(obj => obj.age === age) } )
ハーレーB

62

私は地図を作成して、重複を削除します:

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ループの方が「効率的」です。


3
@elclanrs-「最もパフォーマンスが効率的な方法」-このアプローチは低速です。
トラビスJ

1
@Eevee:なるほど。回答のアンダースコアバージョンもそれほど高速ではありません。つまり、最終的にはより便利なものを選択します。1〜30%程度は、汎用テストの「大幅な改善」と、OP / sが数千に及ぶ場合を反映していると思います。 。
elclanrs 2013

4
確かに。要素が3つだけの場合、3つの変数を作成していくつかifのを使用するのが最も高速です。あなたは300万でいくつかの非常に異なる結果を得るでしょう。
イーブイ2013

1
高速ではありませんが、私の場合、大きなデータセットを扱っていないので、うまくいきました。
フェリペアラルコン2018

37
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]

6
このコードは質問に答えることがありますが、問題を解決する方法理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。
Alexander

indexOfとindexを使用している最初のコードは素晴らしいです。
悲しいことに、

これはプリミティブ値に対してのみ機能することに注意してください。日付の配列がある場合は、さらにカスタマイズされたメソッドが必要です。詳細はこちら:codeburst.io/javascript-array-distinct-5edc93501dc4
Molx

36

キーによって一意のすべてのプロパティを持つオブジェクト返したい人のために

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

const key = 'age';

const arrayUniqueByKey = [...new Map(array.map(item =>
  [item[key], item])).values()];

console.log(arrayUniqueByKey);

   /*OUTPUT
       [
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
       ]
   */

 // Note: this will pick the last duplicated item in the list.


2
注:これにより、リスト内で最後に複製されたアイテムが選択されます。
Eugene Kulabuhov

1
これを答えに追加してください!
Arun Kumar Saini

1
まさに私が探していたもの。共有してくれてありがとう!
Devner

1
正直なところ、これが一番の答えだと思います!
Jaroslav Benc

26

すでに有効な答えはたくさんありますが、その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]

20

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]

1
現実のシナリオで必要とされる唯一の明確な値が年齢である可能性は低いため、私は一般的なソリューションを最も気に入っています。
トフト

5
ジェネリックでは、「distinct.push(key)」を「distinct.push(x)」に変更して、実際に使用できる要素のリストを返します。
Silas Hansen

15

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

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]ます。


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が最適化で最も優れているようです。

上記のスニペットが他のスニペットと比較してなぜ速いのか、正確にはわかりません。おそらく私よりも賢い人が答えを持っています。;-)


9

ロダッシュ使う

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]

2
質問に示されているように、プレーンなJavaScriptでこれを行う方法を説明できますか
Ruskin

それは、アンダースコア関数_.chainある
VINI

2
しかし、pluck()はlodashにはありません。
Yash Vekaria 2016年

1
_.chain(array).map( 'age')。unique()。value(); 私のために働いた。
Yash Vekaria

バージョン4.0は、いくつかの重大な変更を参照していた- stackoverflow.com/a/31740263/4050261
ADARSH Madrecha

7

Lodashの使用

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

_.chain(array).map('age').unique().value();

返品[17,35]


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;
}


5

以下は、reduceを使用し、マッピングを可能にし、挿入順序を維持する多目的なソリューションです。

items:配列

mapper:アイテムを条件にマップする単項関数、またはアイテム自体をマップする場合は空。

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

使用法

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

これをArrayプロトタイプに追加し、それがあなたのスタイルである場合はitemsパラメーターを省略できます...

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

配列の代わりにセットを使用して、照合を高速化することもできます。

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}

5

const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/


2
これはO(n ^ 2)のように見えます
cegprakash

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)})

4

groupBy関数(Lodashを使用)を探していると思います

_personsList = [{"name":"Joe", "age":17}, 
                {"name":"Bob", "age":17}, 
                {"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

結果を生成します:

17,35

jsFiddleデモ:http ://jsfiddle.net/4J2SX/201/


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倍の速さです。



3

私のコードは長さも時間の複雑さもほとんどないことはわかっていますが、理解できるので、この方法を試しました。

ここでプロトタイプベースの関数を開発しようとしています。コードも変更されます。

ここで、Distinctは私自身のプロトタイプ関数です。

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

  Array.prototype.Distinct = () => {
    var output = [];
    for (let i = 0; i < array.length; i++) {
      let flag = true;
      for (let j = 0; j < output.length; j++) {
        if (array[i].age == output[j]) {
          flag = false;
          break;
        }
      }
      if (flag)
        output.push(array[i].age);
    }
    return output;
  }
  //Distinct is my own function
  console.log(array.Distinct());
</script>



1

私の下のコードは、年齢の一意の配列と重複した年齢がない新しい配列を表示します

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

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

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が必要になる場合があります


1

オブジェクト全体の一意が必要な場合

const _ = require('lodash');

var objects = [
  { 'x': 1, 'y': 2 },
  { 'y': 1, 'x': 2 },
  { 'x': 2, 'y': 1 },
  { 'x': 1, 'y': 2 }
];

_.uniqWith(objects, _.isEqual);

[オブジェクト{x:1、y:2}、オブジェクト{x:2、y:1}]


downvoter:なぜ反対票を説明するのか?
cegprakash

1

この古い質問に答えることはかなり無意味ですが、Javascriptの性質を説明する簡単な答えがあります。JavaScriptのオブジェクトは、本質的にハッシュテーブルです。これを使用して、一意のキーのハッシュを取得できます。

var o = {}; array.map(function(v){ o[v.age] = 1; });

次に、ハッシュを一意の値の配列に減らすことができます。

var a2 = []; for (k in o){ a2.push(k); }

それで十分です。配列a2には、一意の年齢だけが含まれています。


1

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

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

@ Jeb50読みやすい複数行を追加しますか?ここで他の人たちを見ると、彼らが読みやすく理解しやすいとは本当に思えません。私はこれを、それが何をするかを説明する関数に置くのが最善だと思います。
クレイグ

2
矢印機能付き:array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i)。私は😊私はそれを見たときに二度のものを読まなければならないということになりましたので、めったに機能キーワードを使用しない
Drenai
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.