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


1016

다음 구조의 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 ]

사용자 정의 유틸리티 기능이 필요하지 않도록 더 우아하거나 관용적 인 방법이 있습니까?


제안 된 duplicate 에 대한 참고 는 단일 객체 를 배열 로 변환하는 방법을 다룹니다 .


4
프로토 타입 라이브러리는 Array 프로토 타입에 "추적 (pluck)"기능을 추가했습니다 var foos = objArray.pluck("foo");.
— Pointy

3
@hyde-jsperf.com/ map - vs- native - for- loop- 이것 좀 봐, 평범한 루핑 자체가 좋은 해결책이 되길 바란다
— N20084753

1
공정한 테스트를 위해서는 @ N20084753 또한 Array.prototype.map존재 하는 기본 기능을 비교해야 합니다.
— Alnitak

@Alnitak-그렇습니다.하지만 기본 Array.prototype.map이 원시 for 루프보다 최적화 된 방법을 이해할 수 없습니다. 우리는 완전한 배열을 통과해야합니다.
— N20084753

3
OP, 나는 제안 된 다른 사람들에게 당신의 접근 방식을 선호합니다. 아무 문제가 없습니다.

답변:


1334

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

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

또는

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

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


3
글쎄, 이것은 totymedli의 다른 답변의 의견과 동일하지만 그럼에도 불구하고 실제로 다른 답변 보다 (나의 의견으로는) 더 낫 습니다.
— hyde


1
물론 허용되지만 IMHO는이 질문에 대답 할 때 사용할 수 없었고 일부 브라우저에서 지원되지 않는 구문을 사용한다는 점을 제외 하고는이 답변을 객관적으로 향상시키는 것은 없습니다 . 또한이 답변은이 답변이 게시되기 거의 1 년 전에 원래 수락 된 답변에 대한 의견의 직접적인 사본입니다.
— Alnitak

26
@Alnitak 내 관점에서 새로운 기능을 사용하는 것이 객관적으로 더 좋습니다. 이 스 니펫은 매우 일반적이므로 표절이라고 확신하지 않습니다. 오래된 답변을 맨 위에 고정시키는 데 실제로 가치가 없습니다.
— Rob

6
의견은 답변이 아닙니다. 누군가가 답변 대신 의견을 게시하는 경우 누군가가 답변에 답변을 복사하면 본인의 잘못입니다.
— Aequitas

618

예, 그러나 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

영업 이익이 방법은 싶어하지 않는 모든 필드를 그냥 하드 코딩하지 foo?
— Alnitak

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 일에 게시되었습니다.
— 어둠의 절대자 니트

52

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

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()있습니다. 결과적으로 다음 두 줄은 LoLos 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)

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루프가 대안보다 성능이 뛰어나다 는 것을 알았 습니다.

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

전통적인 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 / sec

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

TL; DR-.map ()은 느리지 만 가독성이 성능 이상의 가치가 있다고 생각되면 자유롭게 사용하십시오.

편집 # 2 : 6/2019-jsPerf 링크가 끊어졌습니다.


1
나에게 당신이 좋아하는 것을 부르십시오. 그러나 성과는 중요합니다. 맵을 이해할 수 있다면 for 루프를 이해할 수 있습니다.
— illcrx

벤치마킹 결과를 배우는 것이 도움이되지만이 질문에 대한 답이 없기 때문에 여기서는 부적절하다고 생각합니다. 그것을 개선하려면 실제 답변을 추가하거나 주석으로 압축하십시오 (가능한 경우).
— Ifedi Okonkwo

15

사용 Array.prototype.map:

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

ES5 이전 브라우저의 심에 대해서는 위의 링크를 참조하십시오.


15

크로스 브라우저 보증을 위해 lodash 또는 밑줄과 같은 일종의 라이브러리를 사용하는 것이 좋습니다.

Lodash에서는 다음 방법으로 배열의 속성 값을 얻을 수 있습니다.

_.map(objArray,"foo")

밑줄로

_.pluck(objArray,"foo")

둘 다 돌아올 것이다

[1, 2, 3]

10

ES6에서는 다음을 수행 할 수 있습니다.

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

foo가 정의되지 않은 경우는 어떻습니까? 정의되지 않은 배열은 좋지 않습니다.
— godhar

6

map객체 목록에서 '열'을 선택하는 적절한 솔루션 이지만 단점이 있습니다. 열이 존재하는지 여부를 명시 적으로 확인하지 않으면 오류가 발생하고 (최상의)을 제공합니다 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 예제

제공된 목록의 항목 중 하나가 객체가 아니거나 필드를 포함하지 않는 경우에도 작동합니다.

항목이 객체가 아니거나 필드를 포함하지 않는 경우 기본값을 협상하여 더 유연하게 만들 수도 있습니다.

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과 동일합니다. (이 경우 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어떻게 든 난 그냥 구글에 그것을 발견하는 데 실패, 이동하는 obivious 방법이다 (I는 관련이없는 단지 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}왼쪽이 사용 물체 destructoring를 화살표의 우측에 상당하기 {foo: foo, baz: baz}때문 ES6의 향상된 객체 리터럴 .


내가 필요한 것,이 솔루션이 얼마나 빠르거나 느리다고 생각하십니까?
— Ruben

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에 아래와 같은 세 가지 속성이있는 객체가 있다고 가정합니다.

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

이제 모든 객체에서 foo 및 bar 속성을 추출하여 별도의 배열에 저장하려고합니다. 먼저 map을 사용하여 배열 요소를 반복 한 다음 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에서 왔습니까? 표준 기능이 아닙니다.
— 네 베스

방금 답변을 업데이트했습니다. _.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.