자바 스크립트로 객체 배열 축소


225

a.x각 요소에 대해 합계 를 원한다고 가정 해보십시오 arr.

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN

나는 어느 시점에서 도끼가 정의되어 있지 않다고 생각합니다.

다음은 잘 작동합니다

arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7

첫 번째 예에서 내가 뭘 잘못하고 있니?


1
또한 arr.reduce(function(a,b){return a + b})두 번째 예에서 의미 하는 것 같습니다 .
Jamie Wong

1
수정 해 주셔서 감사합니다. 나는 여기서 줄
YXD

6
@Jamie 웡 실제로 자바 스크립트 1.8의 일부입니다
JaredMcAteer

@OriginalSyn 예-방금 봤습니다. 흥미롭지 만 완전한 기본 지원이 없기 때문에 이와 같은 질문에 대답 할 때 구현이 여전히 중요합니다.
Jamie Wong

3
JavaScript 버전은 Firefox 해석기의 버전 일 뿐이므로 참조하기가 혼동됩니다. ES3 및 ES5 만 있습니다.
Raynos

답변:


279

첫 번째 반복 후 숫자를 반환 한 다음 그 속성을 가져 와서 x다음 객체에 추가하고 결과 undefined와 관련된 수학 을 추가하려고합니다 .undefinedNaN

x매개 변수의 x 속성 합계를 가진 속성을 포함하는 객체를 반환 하십시오.

var arr = [{x:1},{x:2},{x:4}];

arr.reduce(function (a, b) {
  return {x: a.x + b.x}; // returns object with property x
})

// ES6
arr.reduce((a, b) => ({x: a.x + b.x}));

// -> {x: 7}

주석에서 추가 된 설명 :

다음 반복에서 변수로 [].reduce사용 된 각 반복의 리턴 값입니다 a.

반복 1 : a = {x:1}, b = {x:2}, {x: 3}에 할당 a반복 2

반복 2 : a = {x:3}, b = {x:4}.

예제의 문제는 숫자 리터럴을 반환한다는 것입니다.

function (a, b) {
  return a.x + b.x; // returns number literal
}

반복 1 : a = {x:1}, b = {x:2}, // returns 3등의 a다음 반복에서

반복 2 : a = 3, b = {x:2}리턴NaN

숫자 리터럴은 3(일반적으로)라는 속성이없는 x것이 그래서 undefinedundefined + b.x반환 NaN하고 NaN + <anything>항상NaN

설명 : 나는 숫자 스레드를 얻는 마법의 숫자로 줄이려면 선택적 매개 변수를 전달하는 것이 더 깨끗하다는 생각에 동의하지 않기 때문에이 스레드의 다른 최상위 답변보다 내 방법을 선호합니다. 줄이 적을 수 있지만 읽을 수는 없습니다.


7
물론 reduce는 배열의 각 요소에 대한 작업을 수행하는 기능을 수행합니다. 연산에서 다음 'a'변수로 사용되는 값을 반환 할 때마다 따라서 첫 번째 반복 a = {x : 1}, b = {x : 2}, 두 번째 반복 a = {x : 3} (첫 번째 반복의 결합 된 값), b = {x : 4}. 두 번째 반복에서 예제의 문제는 3.x + bx를 추가하려고 시도했습니다. 3에는 x라는 속성이 없으므로 정의되지 않은 상태로 반환하고 bx에 추가 한 값 (4)는 숫자가 아님
JaredMcAteer

설명을 이해하지만 여전히 {x : ...}가 어떻게 반복 2가 도끼를 호출하지 못하게하는지 보지 못합니까? x는 무엇을 의미합니까? 나는이 방법을 방법과 함께 사용하려고 시도했지만 작동하지 않는 것 같습니다
mck

실제로 stackoverflow.com/questions/2118123/…을 읽고 어떻게 작동하는지 이해하십시오. 그러나 단순한 속성이 아닌 메소드가있을 때 어떻게 하시겠습니까?
mck

278

이를 달성하는 더 확실한 방법은 초기 값을 제공하는 것입니다.

var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7
console.log(arr);

익명 함수가 처음 호출되면로 호출되어 (0, {x: 1})를 반환합니다 0 + 1 = 1. 다음 번에는로 호출되어 (1, {x: 2})를 반환합니다 1 + 2 = 3. 그런 다음으로 호출되어 (3, {x: 4})마침내을 반환 7합니다.


1
초기 값 (두 번째 감소 매개 변수)
TJ

감사합니다 reduce.에 대한 두 번째 인수가있는 힌트를 찾고있었습니다 .
nilsole

이것은 허용되는 답변보다 코드가 적고 직접적입니다. 허용 된 답변이 이것보다 나은 방법이 있습니까?
jbyrd

1
이것은 배열 길이가 때때로 1 일 때의 상황을 이해하는 데 도움이되었습니다.
HussienK

1
es6 방식 var arr = [{x : 1}, {x : 2}, {x : 4}]; a = arr.reduce ((acc, obj) => acc + obj.x, 0); console.log (a);
amar ghodke

76

TL; DR, 초기 값 설정

파괴 사용

arr.reduce( ( sum, { x } ) => sum + x , 0)

파괴하지 않고

arr.reduce( ( sum , cur ) => sum + cur.x , 0)

Typescript로

arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)

파괴 방법을 사용해 봅시다 :

const arr = [ { x: 1 }, { x: 2 }, { x: 4 } ]
const result = arr.reduce( ( sum, { x } ) => sum + x , 0)
console.log( result ) // 7

이것의 핵심은 초기 값을 설정하는 것입니다. 리턴 값은 다음 반복의 첫 번째 매개 변수가됩니다.

최고 답변에 사용 된 기술은 관용적이지 않습니다

수락 된 답변은 "선택적"값을 전달하지 말 것을 제안합니다. 관용적 인 방법은 두 번째 매개 변수가 항상 포함되어 있기 때문에 이것은 잘못 입니다. 왜? 세 가지 이유 :

1. 위험 -콜백 기능이 부주의 한 경우 초기 값을 전달하지 않으면 위험하며 부작용과 돌연변이가 발생할 수 있습니다.

보다

const badCallback = (a,i) => Object.assign(a,i) 

const foo = [ { a: 1 }, { b: 2 }, { c: 3 } ]
const bar = foo.reduce( badCallback )  // bad use of Object.assign
// Look, we've tampered with the original array
foo //  [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]

그러나 초기 값으로 이런 식으로 수행 한 경우 :

const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }

레코드의 경우, 원래 오브젝트를 변경하지 않으려면 첫 번째 매개 변수를 Object.assign빈 오브젝트로 설정하십시오 . 이와 같이 : Object.assign({}, a, b, c).

2-더 나은 유형 추론 -Typescript와 같은 도구 또는 VS Code와 같은 편집기를 사용하면 컴파일러에 초기를 알리는 이점이 있으며 잘못하면 오류가 발생할 수 있습니다. 초기 값을 설정하지 않으면 많은 상황에서 추측하지 못할 수 있으며 런타임 오류를 초래할 수 있습니다.

3-Functors 존중-JavaScript 는 내부 기능적 자식이 풀릴 때 가장 잘 나타납니다. 기능적 세계에는 "접는"방법이나 reduce배열 에 대한 표준이 있습니다 . 배열에 변형 을 접거나 적용 할 때 해당 배열의 값을 사용하여 새 유형을 구성합니다. 결과 유형을 전달해야합니다. 최종 유형이 배열, 다른 배열 또는 다른 유형의 값인 경우에도이를 수행해야합니다.

다른 방법으로 생각해 봅시다. JavaScript에서 함수는 데이터처럼 전달 될 수 있습니다. 이것이 콜백이 작동하는 방식입니다. 다음 코드의 결과는 무엇입니까?

[1,2,3].reduce(callback)

숫자를 반환합니까? 객체? 이것은 더 명확하게

[1,2,3].reduce(callback,0)

기능 프로그래밍 사양에 대한 자세한 내용은 https://github.com/fantasyland/fantasy-land#foldable을 참조 하십시오.

더 많은 배경

reduce방법에는 두 가지 매개 변수가 사용됩니다.

Array.prototype.reduce( callback, initialItem )

callback함수는 다음 매개 변수를 사용합니다

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

첫 번째 반복의 경우

  • 경우에 initialItem제공되는 상기 reduce기능은 전달 initialItem은 AS accumulator와 같은 배열의 첫 번째 항목 itemInArray.

  • 경우 initialItem되어 있지 제공의 reduce함수로서 배열의 첫 번째 항목이 통과 initialItem하고 상기 어레이의 두 번째 항목 itemInArray동작을 혼동 할 수있다.

항상 Reduce의 초기 값을 설정하는 것을 가르치고 권장합니다.

다음에서 설명서를 확인할 수 있습니다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

도움이 되었기를 바랍니다!


1
파괴는 내가 원하는 것입니다. 매력처럼 작동합니다. 감사합니다!
AleksandrH

24

다른 사람들 이이 질문에 대답했지만 다른 접근법으로 던질 것이라고 생각했습니다. 합산 도끼로 직접 이동하지 않고 (도에서 x로) 맵을 결합하고 (x를 추가하기 위해) 줄일 수 있습니다.

arr = [{x:1},{x:2},{x:4}]
arr.map(function(a) {return a.x;})
   .reduce(function(a,b) {return a + b;});

분명히 조금 느려질 것입니다.하지만 옵션으로 언급 할 가치가 있다고 생각했습니다.


8

지적 된 것을 공식화하기 위해 감속기는 우연에 의해 동일한 유형일 수있는 두 개의 인수를 취하고 첫 번째 인수와 일치하는 유형을 리턴하는 이변 형입니다.

function reducer (accumulator: X, currentValue: Y): X { }

이것은 감속기의 몸체가 변환에 관한 것이어야 currentValue하고의 현재 값을 accumulator새로운 값으로 변환해야한다는 것을 의미합니다 accumulator.

누산기와 요소 값이 모두 같은 유형이지만 다른 목적으로 사용되기 때문에 이것은 추가 할 때 간단하게 작동합니다.

[1, 2, 3].reduce((x, y) => x + y);

그들은 모두 숫자이기 때문에 작동합니다.

[{ age: 5 }, { age: 2 }, { age: 8 }]
  .reduce((total, thing) => total + thing.age, 0);

이제 애그리 게이터에게 시작 가치를 부여합니다. 시작 값은 대부분의 경우 애그리 게이터가 될 것으로 예상되는 유형 (최종 값으로 나타날 것으로 예상되는 유형)이어야합니다. 이 작업을 수행하도록 강요받지 말아야하지만 그렇게해서는 안됩니다.하지만 명심해야합니다.

일단 알면 다른 n : 1 관계 문제에 대한 의미있는 축소를 작성할 수 있습니다.

반복되는 단어 제거하기 :

const skipIfAlreadyFound = (words, word) => words.includes(word)
    ? words
    : words.concat(word);

const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);

찾은 모든 단어의 수를 제공합니다 :

const incrementWordCount = (counts, word) => {
  counts[word] = (counts[word] || 0) + 1;
  return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });

배열 배열을 단일 평면 배열로 줄이기 :

const concat = (a, b) => a.concat(b);

const numbers = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
].reduce(concat, []);

일의 배열에서 1 : 1과 일치하지 않는 단일 값으로 갈 때마다 감소가 고려됩니다.

실제로 맵과 필터는 모두 축소로 구현할 수 있습니다.

const map = (transform, array) =>
  array.reduce((list, el) => list.concat(transform(el)), []);

const filter = (predicate, array) => array.reduce(
  (list, el) => predicate(el) ? list.concat(el) : list,
  []
);

이것이 사용 방법에 대한 추가 컨텍스트를 제공하기를 바랍니다 reduce.

내가 아직 다루지 않은 이것에 대한 한 가지 추가 사항은 배열 요소가 함수이기 때문에 입력 및 출력 유형이 특히 동적이어야한다는 기대가있을 때입니다.

const compose = (...fns) => x =>
  fns.reduceRight((x, f) => f(x), x);

const hgfx = h(g(f(x)));
const hgf = compose(h, g, f);
const hgfy = hgf(y);
const hgfz = hgf(z);

1
형식 시스템 측면에서 명확하게 설명하기 위해 +1 OTOH, 나는 그것이 변증이 많은 사람들에게 전염 될 수 있다는 생각으로 시작함으로써 생각합니다.
Periata Breatta

6

첫 번째 반복에서 'a'는 배열의 첫 번째 객체이므로 ax + bx는 1 + 2 즉 3을 반환합니다. 이제 다음 반복에서 반환 된 3은 a에 할당되므로 a는 n을 호출하는 숫자입니다. NaN을 줄 것이다.

간단한 해결책은 먼저 숫자를 배열로 매핑 한 다음 아래와 같이 줄입니다.

arr.map(a=>a.x).reduce(function(a,b){return a+b})

여기 arr.map(a=>a.x)에 숫자 배열 [1,2,4] .reduce(function(a,b){return a+b})을 제공 할 것입니다.

또 다른 간단한 해결책은 아래와 같이 0을 'a'에 할당하여 초기 합계를 0으로 제공하는 것입니다.

arr.reduce(function(a,b){return a + b.x},0)

5

Reduce의 각 단계에서 새 {x:???}객체를 반환하지 않습니다 . 따라서 다음 중 하나를 수행해야합니다.

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a + b.x})

또는해야 할 일

arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return {x: a.x + b.x}; }) 

3
첫 번째 예제에는 기본값 (예 : 0)이 필요합니다. 그렇지 않으면 첫 번째 반복에서 "a"가 정의되지 않습니다.
그리핀

2

첫 번째 단계에서는 값 a이 1이고 b2 의 값은 2이지만 2 + 1이 반환되고 다음 단계에서는 값이 b반환 값 from step 1 i.e 3이므로 b.x정의되지 않습니다. .and undefined + anyNumber는 NaN이되므로 그 결과를 얻습니다.

대신 초기 값을 0으로 지정하여 시도해 볼 수 있습니다.

arr.reduce(function(a,b){return a + b.x},0);


접근 방식이 다른 방식과 어떻게 다른지 보여주기 위해 이것을 업데이트하십시오.
kwelch December

2

객체 배열과 같이 많은 양의 데이터가있는 복잡한 객체가있는 경우 단계별 접근 방식으로이 문제를 해결할 수 있습니다.

예를 들어 :

const myArray = [{ id: 1, value: 10}, { id: 2, value: 20}];

먼저, 배열을 원하는 새 배열로 매핑해야합니다.이 예에서는 값의 새 배열이 될 수 있습니다.

const values = myArray.map(obj => obj.value);

이 콜백 함수는 원래 배열의 값만 포함하는 새 배열을 반환하고 값 const에 저장합니다. 이제 값 const는 다음과 같은 배열입니다.

values = [10, 20];

이제 축소를 수행 할 준비가되었습니다.

const sum = values.reduce((accumulator, currentValue) => { return accumulator + currentValue; } , 0);

보다시피, reduce 메소드는 콜백 함수를 여러 번 실행합니다. 매번 배열에서 항목의 현재 값을 가져와 누산기와 합산합니다. 따라서 올바르게 합산하려면 누산기의 초기 값을 reduce 메서드의 두 번째 인수로 설정해야합니다.

이제 값이 30 인 새 const 합계가 있습니다.


0

컬렉션에서 반복되는 함수 축소

arr = [{x:1},{x:2},{x:4}] // is a collection

arr.reduce(function(a,b){return a.x + b.x})

다음과 같이 번역됩니다.

arr.reduce(
    //for each index in the collection, this callback function is called
  function (
    a, //a = accumulator ,during each callback , value of accumulator is 
         passed inside the variable "a"
    b, //currentValue , for ex currentValue is {x:1} in 1st callback
    currentIndex,
    array
  ) {
    return a.x + b.x; 
  },
  accumulator // this is returned at the end of arr.reduce call 
    //accumulator = returned value i.e return a.x + b.x  in each callback. 
);

각 인덱스 콜백 동안 변수 "accumulator"의 값은 콜백 함수의 "a"매개 변수로 전달됩니다. "누적 기"를 초기화하지 않으면 값이 정의되지 않습니다. undefined.x를 호출하면 오류가 발생합니다.

이 문제를 해결하려면 Casey의 답변이 위에 표시된대로 값 0 으로 "누산기"를 초기화하십시오 .

"감소"함수의 인-아웃을 이해하려면이 함수의 소스 코드를 살펴 보는 것이 좋습니다. Lodash 라이브러리는 ES6의 "reduce"기능과 정확히 동일한 기능을 줄입니다.

여기 링크가 있습니다 : 소스 코드를 줄입니다


0

모든 x소품 의 합계를 반환하려면 :

arr.reduce(
(a,b) => (a.x || a) + b.x 
)

3
이 답변이 왜 더 나은지에 대한 컨텍스트를 제공하면 다른 승인되거나 공표 된 답변이 사용자가 질문에 대한 솔루션을 찾을 때 도움이 될 수 있습니다.
앤드류

0

당신은지도를 사용하고 기능을 줄일 수 있습니다

const arr = [{x:1},{x:2},{x:4}];
const sum = arr.map(n => n.x).reduce((a, b) => a + b, 0);

-1

배열 축소 함수는 initialValue (기본값은 0), 누산기 및 현재 값의 세 가지 매개 변수를 사용합니다. 기본적으로 initialValue 값은 "0"입니다. 누산기에 의해 촬영

코드에서 이것을 보자.

var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal ) ; 
// (remember Initialvalue is 0 by default )

//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks .
// solution = 7

이제 초기 값과 동일한 예 :

var initialValue = 10;
var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal,initialValue ) ; 
/
// (remember Initialvalue is 0 by default but now it's 10 )

//first iteration** : 10 +1 => Now accumulator =11;
//second iteration** : 11 +2 => Now accumulator =13;
//third iteration** : 13 + 4 => Now accumulator = 17;
No more array properties now the loop breaks .
//solution=17

객체 배열에도 동일하게 적용됩니다 (현재 스택 오버 플로우 질문).

var arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(acc,currVal){return acc + currVal.x}) 
// destructing {x:1} = currVal;
Now currVal is object which have all the object properties .So now 
currVal.x=>1 
//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks 
//solution=7

마음에 드는 한 가지는 InitialValue이며 기본적으로 0이며 {}, [] 및 숫자를 의미하는 모든 것을 제공 할 수 있습니다.


-1

    
  
 //fill creates array with n element
 //reduce requires 2 parameter , 3rd parameter as a length
 var fibonacci = (n) => Array(n).fill().reduce((a, b, c) => {
      return a.concat(c < 2 ? c : a[c - 1] + a[c - 2])
  }, [])
  console.log(fibonacci(8))


-2

어큐뮬레이터에 ax를 사용하면 안됩니다. 대신`arr = [{x : 1}, {x : 2}, {x : 4}]와 같이 할 수 있습니다

arr.reduce (함수 (a, b) {a + bx}, 0)`

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.