web-dev-qa-db-ja.com

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

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

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

キーfooの値を含む配列を抽出したいと思います。その結果、値は[ 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 ]

達成するためのよりエレガントでパフォーマンスの高い方法はありますか?


提案された複製について - その質問はオブジェクトを変換する方法を尋ねる to 配列、この質問は単一のプロパティを抽出する方法を尋ねる from オブジェクトの配列。

593
hyde

これを達成するためのより短い方法は次のとおりです。

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

または 

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

Array.prototype.map() MDNのドキュメントもチェックできます

667
Jalasem

はい、それはJavaScriptのES5機能に依存しています。これは、IE 8以前では機能しないことを意味します。

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

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

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

ドキュメンテーション

563

Lodashの_.pluck() functionまたは Underscoreの_.pluck() functionをチェックしてください。どちらも、1回の関数呼び出しで必要なことをすべて実行します。

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

更新: _.pluck()はLodash v4.0.0以降で削除されました_.map()に類似したものを組み合わせて Niet's answer_.pluck()はUnderscoreでまだ利用可能です

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

さらに、_.map()では、_.property()に渡される2番目のパラメータとして文字列を渡すことができるようになりました。結果として、次の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]を返します。

もしあなたがもう少し関数型プログラミングに興味があるなら、 Ramda'sR.pluck() functionを見てください。

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

JSのみの解決策と言えば、私は、単純でないインデックス付きのforループが、他の選択肢よりもパフォーマンスが高いことを、私は気づいたことです。

https://jsperf.com/extract-prop-from-object-array/ /

100000要素配列から単一のプロパティを抽出する

トラディショナルforループ 368 Ops/sec

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

ES6 for..ofループ 303 Ops/sec

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

Array.prototype.map 19 Ops /秒

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

編集:Ops/sが2017年10月10日に更新されました。 TL; DR - .map()が遅いしかし、読みやすさはパフォーマンスよりも価値があることがあります。

32
pscl

Array.prototype.map :を使う

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

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

15
Alnitak

クロスブラウザ保証のためには、lodashやアンダースコアのようなある種のライブラリを使うのが良いでしょう。

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

_.map(objArray,"foo")

そしてアンダースコアで 

_.pluck(objArray,"foo")

両方とも[1、3、5]を返します

9
Tejas Patel

mapはオブジェクトのリストから 'columns'を選択するための適切な解決策ですが、欠点があります。列が存在するかどうかを明示的にチェックしないと、エラーが発生し、(せいぜい)undefinedが返されます。 私はreduceソリューションを選びたいと思います。これは単にプロパティを無視するか、あるいはデフォルト値であなたを設定することさえできます。

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の例

返される配列の長さは提供された配列と同じになるため、これはmapと同じになります。 (その場合、mapreduceよりわずかに安いです):

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の例

そして、最も柔軟な解決策があります。1つは、代わりの値を提供することによって両方の動作を切り替えることができるというものです。

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の例

5
Rogier Spieker

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

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

それはあなたの "より良い"の定義によります。

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

その一方で、この美しさは追加費用なしでは実現できません。私はマイクロベンチの大ファンではありませんが、ここで小さな テストを立ち上げました 。結果は予測可能です、古い醜い方法はmap関数より速いようです。したがって、「より良い」が「より速い」を意味するのであれば、いいえ、古い学校のファッションにとどまります。

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

2
sitifensys

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

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

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

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

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

ES6

let a = [{ z: 'Word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, x) => [...acc, Object.values(x).map((y, i) => 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"]

1
eugen sunic

関数配列はオブジェクト配列を扱うときには良い選択です。すでにたくさんの良い答えが投稿されていますが、filterと組み合わせてmapを使う例は役に立つかもしれません。

値が未定義のプロパティや特定のプロパティだけを除外したい場合は、次のようにします。

    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/ /

1
Niko Gamulin

上記の答えは、単一のプロパティを抽出するのに適しています。 オブジェクトの配列から複数のプロパティを抽出する場合はどうなりますか。 これが解決策です!! その場合は、単に_.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プロパティを抽出し、それらを別々の配列に格納したいと思います。 

これで、 '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}]になります。

楽しい!!!

0
Akash Jain

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}オブジェクトデストラクタリング を使用し、矢印の右側が ES6の強化されたオブジェクトリテラル のため{foo: foo, baz: baz}と同等であるため、機能します。

0
Chris Magnuson