次のような構造の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 オブジェクトの配列。
これを達成するためのより短い方法は次のとおりです。
let result = objArray.map(a => a.foo);
または
let result = objArray.map(({ foo }) => foo)
Array.prototype.map() MDNのドキュメントもチェックできます
はい、それはJavaScriptのES5機能に依存しています。これは、IE 8以前では機能しないことを意味します。
var result = objArray.map(function(a) {return a.foo;});
ES6互換のJSインタプリタでは、簡潔にするために の矢印関数 を使用できます。
var result = objArray.map(a => a.foo);
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)
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()が遅いしかし、読みやすさはパフォーマンスよりも価値があることがあります。
Array.prototype.map
:を使う
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
ES5以前のブラウザのシムについては、上記のリンクを参照してください。
クロスブラウザ保証のためには、lodashやアンダースコアのようなある種のライブラリを使うのが良いでしょう。
Lodashでは、次の方法で配列のプロパティの値を取得できます。
_.map(objArray,"foo")
そしてアンダースコアで
_.pluck(objArray,"foo")
両方とも[1、3、5]を返します
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;
}, []);
}
提供されたリスト内の項目の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;
}, []);
}
返される配列の長さは提供された配列と同じになるため、これはmapと同じになります。 (その場合、map
は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;
}, []);
}
そして、最も柔軟な解決策があります。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;
}, []);
}
上の例が(うまくいけば)これが機能する方法にいくらかの光を当てるように、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 : []));
}, []);
}
ES6では、次のことができます。
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
それはあなたの "より良い"の定義によります。
他の答えはmapの使用を指摘します、それは自然で(特に機能的なスタイルに慣れている人のために)そして簡潔です。私はそれを使用することを強くお勧めします(あなたが数人のIE8-ITの人に悩まされていない場合)。したがって、「より良い」が「より簡潔」、「保守可能」、「理解可能」を意味するのであれば、はい、それははるかに優れています。
その一方で、この美しさは追加費用なしでは実現できません。私はマイクロベンチの大ファンではありませんが、ここで小さな テストを立ち上げました 。結果は予測可能です、古い醜い方法はmap関数より速いようです。したがって、「より良い」が「より速い」を意味するのであれば、いいえ、古い学校のファッションにとどまります。
繰り返しになりますが、これは単なるマイクロベンチであり、map
の使用に反対するものではありません。私の2セントに過ぎません。
配列のようなオブジェクトもサポートしたい場合は、 Array.from を使用します(ES2015)。
Array.from(arrayLike, x => x.foo);
Array.prototype.map() メソッドよりも優れている点は、入力に Set を指定できることです。
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
一般に、(質問で説明したように)配列内にあるオブジェクト値を推定したい場合は、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"]
関数配列はオブジェクト配列を扱うときには良い選択です。すでにたくさんの良い答えが投稿されていますが、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]});
上記の答えは、単一のプロパティを抽出するのに適しています。 オブジェクトの配列から複数のプロパティを抽出する場合はどうなりますか。 これが解決策です!! その場合は、単に_.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}]になります。
楽しい!!!
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}
と同等であるため、機能します。