Programing

객체 배열에서 속성 값을 배열로 추출

lottogame 2020. 9. 29. 07:10
반응형

객체 배열에서 속성 값을 배열로 추출


다음 구조의 JavaScript 개체 배열이 있습니다.

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

각 개체에서 필드를 추출하고 값을 포함하는 배열을 가져오고 싶습니다 . 예를 들어 field 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 ]

이를 수행하는 더 우아하거나 관용적 인 방법이 있으므로 사용자 정의 유틸리티 기능이 필요하지 않습니까?


중복 제안 에 대한 참고 사항은 단일 객체 를 배열 로 변환하는 방법을 다룹니다 .


이를 달성하는 더 짧은 방법은 다음과 같습니다.

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

또는

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

당신은 또한 확인할 수 있습니다 Array.prototype.map().


예,하지만 JavaScript의 ES5 기능에 의존합니다. 즉, IE8 또는 이전 버전에서는 작동하지 않습니다.

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

ES6 호환 JS 인터프리터 에서는 간결성을 위해 화살표 기능사용할 수 있습니다 .

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

Array.prototype.map 문서


Lodash의_.pluck() 기능 또는 Underscore의_.pluck() 기능을 확인하십시오 . 둘 다 단일 함수 호출에서 원하는 것을 정확하게 수행합니다!

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

업데이트 : Niet의 답변 과 유사한 것과 결합 하여 _.pluck()Lodash v4.0.0에서 제거되었습니다 . Underscore에서 계속 사용할 수 있습니다 ._.map()_.pluck()

업데이트 2 : Mark 가 Lodash v4와 4.3 사이 의 주석 에서 지적했듯이이 기능을 다시 제공하는 새로운 기능이 추가되었습니다. _.property()객체의 속성 값을 가져 오는 함수를 반환하는 속기 함수입니다.

또한 _.map()이제 두 번째 매개 변수로 문자열을 전달할 수 있습니다 _.property(). 결과적으로 다음 두 줄은 위의 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의 R.pluck() 함수를 살펴보십시오.

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

JS 전용 솔루션에 대해 말하면서, 단순 인덱스 for루프가 대안보다 성능이 더 뛰어나다 는 것을 알았 습니다.

100000 요소 배열에서 단일 속성 추출 (jsPerf를 통해)

Traditional for loop 368 Ops/sec

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

ES6 for..of loop 303 Ops/sec

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

Array.prototype.map 19 Ops/sec

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

TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.

Edit #2: 6/2019 - jsPerf link broken, removed.


Using Array.prototype.map:

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

See the above link for a shim for pre-ES5 browsers.


It is better to use some sort of libraries like lodash or underscore for cross browser assurance.

In Lodash you can get values of a property in array by following method

_.map(objArray,"foo")

and in Underscore

_.pluck(objArray,"foo")

Both will return

[1, 2, 3]

In ES6, you can do:

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

While map is a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined. I'd opt for a reduce solution, which can simply ignore the property or even set you up with a default value.

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 example

This would work even if one of the items in the provided list is not an object or does not contain the field.

It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.

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 example

This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a map is slightly cheaper than 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 example

And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.

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 example

As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concat function.

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 example


It depends of your definition of "better".

The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.

In the other hand, this beauty don't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The result are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.

Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).


If you want to also support array-like objects, use Array.from (ES2015):

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

The advantage it has over Array.prototype.map() method is the input can also be a Set:

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

In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and 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)

The equivalent using for in loop would be:

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

Produced output: ["word", "again", "some", "1", "2", "3"]


Function map is a good choice when dealing with object arrays. Although there have been a number of good answers posted already, the example of using map with combination with filter might be helpful.

In case you want to exclude the properties which values are undefined or exclude just a specific property, you could do the following:

    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/


Above provided answer is good for extracting single property, what if you want to extract more than one property from array of objects. Here is the solution!! In case of that we can simply use _.pick(object, [paths])

_.pick(object, [paths])

Lets assume objArray has objects with three properties like below

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

Now we want to extract foo and bar property from every object and store them in a separate array. First we will iterate array elements using map and then we apply Lodash Library Standard _.pick() method on it.

Now we are able to extract 'foo' and 'bar' property.

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

and result would be [{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]

enjoy!!!


If you want multiple values in ES6+ the following will work

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

This works as {foo, baz} on the left is using object destructoring and on the right side of the arrow is equivalent to {foo: foo, baz: baz} due to ES6's enhanced object literals.

참고URL : https://stackoverflow.com/questions/19590865/from-an-array-of-objects-extract-value-of-a-property-as-array

반응형