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


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.