オブジェクトの配列から、プロパティの値を配列として抽出します


1019

次の構造のJavaScriptオブジェクト配列があります。

objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];

各オブジェクトからフィールドを抽出し、値を含む配列を取得します。たとえば、フィールドfooはarrayになり[ 1, 3, 5 ]ます。

この簡単なアプローチでこれを行うことができます:

function getFields(input, field) {
    var output = [];
    for (var i=0; i < input.length ; ++i)
        output.push(input[i][field]);
    return output;
}

var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]

これを行うためのよりエレガントなまたは慣用的な方法があるので、カスタムユーティリティ関数は不要になりますか?


推奨される重複について注意してください。これは、単一のオブジェクトを配列に変換する方法をカバーしています


4
Prototypeライブラリは、アレイのプロトタイプに「pluck」関数を追加したと思います(私はそう思います)var foos = objArray.pluck("foo");
先のとがった

3
@hyde-jsperf.com/ map - vs- native - for- loop-これを見てください。プレーンループ自体が良い解決策であることを願っています
N20084753 2013年

1
@ N20084753公正なテストのために、ネイティブArray.prototype.map機能が存在する場所も比較する必要があります
Alnitak 2013年

@Alnitak-ええ、あなたは正しいですが、ネイティブのArray.prototype.mapがネイティブのforループよりもどのように最適化されているのか理解できません。
N20084753 2013年

3
OP、提案されている他のアプローチよりもあなたのアプローチを好む。何も問題はありません。

回答:


1336

これを実現する短い方法を次に示します。

let result = objArray.map(a => a.foo);

または

let result = objArray.map(({ foo }) => foo)

あなたもチェックすることができArray.prototype.map()ます。


3
さて、これはtotymedliによる別の回答のコメントと同じですが、それでも実際には他の回答よりも(私の意見では)方が良いので、...受け入れられた回答に変更します。
hyde


1
確かに許可されていますが、私見では、この回答を客観的に改善するものは何もありません。ただし、質問の時点では利用できず、一部のブラウザーでさえサポートされていない構文を使用しています。また、この回答は、この回答が投稿されるほぼ1年前に最初に受け入れられた回答に対して行われたコメントの直接のコピーであることにも注意します。
アルニタク2018

26
@Alnitak私の観点では、新しい機能を使用すること客観的に優れています。このスニペットは非常に一般的であるため、これが盗作であるとは確信していません。時代遅れの回答をトップに固定しておくことには、何の価値もありません。
ロブ

6
コメントは答えではありません。誰かが答えの代わりにコメントを投稿する場合、誰かがそれを答えにコピーするのは自分の責任です。
Aequitas

619

はい。ただし、JavaScriptのES5機能に依存しています。つまり、IE8以前では機能しません。

var result = objArray.map(function(a) {return a.foo;});

ES6互換のJSインタープリターでは、簡潔にするためにアロー関数を使用できます。

var result = objArray.map(a => a.foo);

Array.prototype.mapドキュメント


5
このソリューションはきちんとしていてシンプルなようですが、単純なループ方式よりも最適化されています。
N20084753 2013年

OPは、ハードコードされただけでなくフィールドを取得するメソッドを必要としませんfooか?
Alnitak 2013年

2
ES6でできることvar result = objArray.map((a) => (a.foo));
Black

28
@Blackさらに良い:var result = objArray.map(a => a.foo);
totymedli

4
@FaizKhan年を通知します。この回答は2013年10月25日に投稿されました。「承認された」回答は2017年10月11日に投稿されました。
Niet the Dark Absol 2017年

52

Lodashの_.pluck()関数またはUnderscoreの_.pluck()関数を確認してください。どちらも、1回の関数呼び出しで必要な機能を実行します。

var result = _.pluck(objArray, 'foo');

更新: _.pluck()Lodash v4.0.0から削除されました。Niet の回答に_.map()似たものと組み合わせることをお勧めします_.pluck()アンダースコアでも引き続き利用できます

更新2: Mark がコメントで指摘したように、Lodash v4と4.3の間のどこかで、この機能を再び提供する新しい関数が追加されました。_.property()オブジェクトのプロパティの値を取得するための関数を返す簡略関数です。

さらに、_.map()に渡される2番目のパラメータとして文字列を渡すことができるようになりました_.property()。その結果、次の2行は、Lodash 4より前のコードサンプルと同じです。

var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));

_.property()、したがって_.map()、サブプロパティにアクセスするために、ドットで区切られた文字列または配列を提供することもできます。

var objArray = [
    {
        someProperty: { aNumber: 5 }
    },
    {
        someProperty: { aNumber: 2 }
    },
    {
        someProperty: { aNumber: 9 }
    }
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));

_.map()上記の例の両方の呼び出しは戻り[5, 2, 9]ます。

関数型プログラミングについてもう少し詳しく知り R.pluck()たい場合は、次のようなRamdaの関数を見てください。

var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)

5
朗報:Lodash 4.0.0と4.3.0の間のどこか_.property('foo')lodash.com/docs#property)がの省略形として追加されましたfunction(o) { return o.foo; }。これは短くへLodashユースケースvar result = _.pluck(objArray, _.property('foo'));さらに便宜のために、Lodash 4.3.0の_.map() 方法はまた、使用速記でき_.property()、その結果、フードの下にvar result = _.map(objArray, 'foo');
マークA. Fitzgeraldの

45

JSのみのソリューションについて言えば、エレガントではないかもしれませんが、単純なインデックス付きforループの方が他のループよりもパフォーマンスが高いことがわかりました。

(jsPerfを介して)100000要素の配列から単一のプロパティを抽出する

従来のforループ 368 Ops /秒

var vals=[];
for(var i=0;i<testArray.length;i++){
   vals.push(testArray[i].val);
}

ES6 for..ofループ 303 オペレーション /秒

var vals=[];
for(var item of testArray){
   vals.push(item.val); 
}

Array.prototype.map 19 オペレーション /秒

var vals = testArray.map(function(a) {return a.val;});

TL; DR-.map()は低速ですが、読みやすさがパフォーマンスよりも価値があると感じた場合は、自由に使用してください。

編集#2:6/2019-jsPerfリンクが壊れ、削除されました。


1
好きなように呼んでください、しかしパフォーマンスは重要です。マップを理解できれば、forループを理解できます。
illcrx

ベンチマーク結果を知ることは有用ですが、この質問には答えられないため、ここでは不適切だと思います。改善するには、実際の回答も追加するか、可能であればそれをコメントに圧縮してください。
Ifedi Okonkwo

16

クロスブラウザーの保証には、lodashやアンダースコアなどのライブラリを使用することをお勧めします。

Lodashでは、次の方法で配列のプロパティの値を取得できます

_.map(objArray,"foo")

アンダースコアで

_.pluck(objArray,"foo")

両方が戻ります

[1, 2, 3]

15

使用Array.prototype.map

function getFields(input, field) {
    return input.map(function(o) {
        return o[field];
    });
}

ES5より前のブラウザーのシムについては、上記のリンクを参照してください。


10

ES6では、次のことができます。

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)

fooが未定義の場合はどうでしょうか?未定義の配列はダメです。
godhar

6

一方でmap、オブジェクトのリストから「列」を選択するための適切な解決策である、それは欠点があります。列が存在するかどうかを明示的にチェックしないと、エラーがスローされ、(よくても)が表示されますundefinedreduceプロパティを無視するか、デフォルト値を設定することもできるソリューションを選びます。

function getFields(list, field) {
    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  check if the item is actually an object and does contain the field
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbinの例

これは、提供されたリストの項目の1つがオブジェクトではないか、フィールドが含まれていない場合でも機能します。

アイテムがオブジェクトではない場合やフィールドが含まれていない場合にデフォルト値をネゴシエートすることで、より柔軟にすることもできます。

function getFields(list, field, otherwise) {
    //  reduce the provided list to an array containing either the requested field or the alternative value
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbinの例

返される配列の長さは提供された配列と同じであるため、これはマップと同じです。(その場合、a mapはaより少し安いですreduce):

function getFields(list, field, otherwise) {
    //  map the provided list to an array containing either the requested field or the alternative value
    return list.map(function(item) {
        //  If item is an object and contains the field, add its value and the value of otherwise if not
        return typeof item === 'object' && field in item ? item[field] : otherwise;
    }, []);
}

jsbinの例

そして、最も柔軟なソリューションがあり、代替値を提供するだけで両方の動作を切り替えることができます。

function getFields(list, field, otherwise) {
    //  determine once whether or not to use the 'otherwise'
    var alt = typeof otherwise !== 'undefined';

    //  reduce the provided list to an array only containing the requested field
    return list.reduce(function(carry, item) {
        //  If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
        if (typeof item === 'object' && field in item) {
            carry.push(item[field]);
        }
        else if (alt) {
            carry.push(otherwise);
        }

        //  return the 'carry' (which is the list of matched field values)
        return carry;
    }, []);
}

jsbinの例

上記の例が(うまくいけば)これが機能する方法に光を当てるので、関数を利用してArray.concat関数を少し短くしましょう。

function getFields(list, field, otherwise) {
    var alt = typeof otherwise !== 'undefined';

    return list.reduce(function(carry, item) {
        return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
    }, []);
}

jsbinの例


6

一般に、配列内にあるオブジェクト値を推定したい場合は(質問で説明されているように)、reduce、map、array destructuringを使用できます。

ES6

let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);

console.log(b)

for inループを使用する同等の方法次のとおりです。

for (let i in a) {
  let temp = [];
  for (let j in a[i]) {
    temp.push(a[i][j]);
  }
  array.push(temp);
}

生成される出力: ["word"、 "again"、 "some"、 "1"、 "2"、 "3"]


3

それは「より良い」というあなたの定義に依存します。

他の答えは、マップの使用を指摘しています。これは自然で(特に機能的なスタイルに慣れている人にとって)、簡潔です。私はそれを使用することを強くお勧めします(あなたがいくつかのIE8- ITの人に迷惑をかけない場合)。したがって、「より良い」が「より簡潔」、「保守可能」、「理解可能」を意味する場合、はい、それははるかに優れています。

一方、この美しさは追加コストなしでは実現できません。私はマイクロベンチの大ファンではありませんが、ここで小さなテストを行いました。結果は予測可能であり、古い醜い方法はマップ機能よりも速いようです。したがって、「より良い」が「より速い」ことを意味する場合は、いいえ、古い学校のファッションに留まります。

繰り返しますが、これは単なるマイクロベンチであり、の使用に反対することは決してありませんmap。それは私の2セントにすぎません:)。


まあ、これは実際にはブラウザではなくサーバー側なので、IE8は無関係です。mapうん、それは行くためのあからさまな方法です、どういうわけか私はグーグルでそれを見つけることができませんでした(私はjqueryのマップだけを見つけました、これは関係ありません)。
hyde

3

配列のようなオブジェクトもサポートする場合は、Array.from(ES2015)を使用します。

Array.from(arrayLike, x => x.foo);

Array.prototype.map()メソッドよりも優れている点は、入力をSetにすることもできます。

let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);

2

ES6 +で複数の値が必要な場合、以下が機能します

objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];

let result = objArray.map(({ foo, baz }) => ({ foo, baz }))

これ{foo, baz}は、左側がオブジェクトのデストラクタを使用しており、矢印の右側{foo: foo, baz: baz}ES6の拡張オブジェクトリテラルによるものと同等であるため、機能します


私が必要とするものだけ、このソリューションはどのくらい速い/遅いと思いますか?
ルーベン

1
@Ruben実際に言うと、このページからさまざまなオプションを取り、jsperf.comの
Chris Magnuson

1

関数配列は、オブジェクト配列を扱う場合に適しています。すでに多くの良い回答が投稿されていますが、フィルターと組み合わせてマップを使用する例は役立つかもしれません。

値が定義されていないプロパティを除外する場合、または特定のプロパティのみを除外する場合は、次のようにします。

    var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
    var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
    var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});

https://jsfiddle.net/ohea7mgk/


0

上記の回答は、単一のプロパティを抽出するのに適しています。オブジェクトの配列から複数のプロパティを抽出する場合はどうでしょうか。 これが解決策です!! その場合、単純に_.pick(object、[paths])を使用できます

_.pick(object, [paths])

objArrayに、以下のような3つのプロパティを持つオブジェクトがあるとしましょう

objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];

次に、すべてのオブジェクトからfooおよびbarプロパティを抽出し、それらを個別の配列に格納します。最初にマップを使用して配列要素を反復処理し、次にLodash Library Standard _.pick()メソッドをそれに適用します。

これで、「foo」および「bar」プロパティを抽出できます。

var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])}) console.log(newArray);

結果は[{foo:1、bar:2}、{foo:3、bar:4}、{foo:5、bar:6}]になります

楽しい!!!


1
どこ_.pickから来たの?標準機能ではありません。
neves

私はちょうど答えを更新しました、_。pick()はLodashライブラリの標準メソッドです。
Akash Jain

0

ネストされた配列がある場合は、次のように機能させることができます。

const objArray = [ 
     { id: 1, items: { foo:4, bar: 2}},
     { id: 2, items: { foo:3, bar: 2}},
     { id: 3, items: { foo:1, bar: 2}} 
    ];

    let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
    
    console.log(result)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.