reduce () 메서드를 조기에 중단하는 방법은 무엇입니까?


94

reduce()메서드 반복을 어떻게 끊을 수 있습니까?

for:

for (var i = Things.length - 1; i >= 0; i--) {
  if(Things[i] <= 0){
    break;
  }
};

reduce()

Things.reduce(function(memo, current){
  if(current <= 0){
    //break ???
    //return; <-- this will return undefined to memo, which is not what I want
  }
}, 0)

current위의 코드 는 무엇입니까 ? 나는 이것들이 어떻게 똑같은 일을 할 수 있는지 모르겠습니다. 어떤 경우에는 초기에 같은 휴식 방법이있다 some, every,find
elclanrs

someevery논리 값을 반환하고 find단일 레코드를 반환, 내가 원하는 메모를 생성하는 작업을 실행하는 것입니다. currentcurrentValue입니다. 참조
훌리오 마린에게

나는 무엇을 의미하는 current코드의 첫 조각에?
elclanrs

답장을 업데이트 덕분에
훌리오 마린

2
대답은에서 일찍 중단 할 수없고 reduce, 일찍 종료하거나 자신 만의 도우미를 만들거나 lodash 등을 사용하는 내장 함수로 다른 방법을 찾아야한다는 것입니다. 하고 싶은 일의 전체 예를 게시 할 수 있습니까?
elclanrs

답변:


94

최신 정보

일부 해설자는 .reduce()논리 내부를 조기에 중단하기 위해 원래 배열이 변형되고 있다는 좋은 지적을합니다 .

따라서 후속 단계 를 호출하기 전에 a를 추가 하여 원래 배열의 복사본을 생성 하여 대답을 약간 수정했습니다 . 참고 : 동일한 작업을 수행하는 유사한 작업은 (덜 명시 적) 및 분산 연산자 ( 약간 성능이 낮음 )입니다. 이들 모두는 전체 런타임 + 1 * (O (1))에 선형 시간의 추가 상수 계수를 추가합니다..slice(0).reduce()slice()[...array]

사본은 반복에서 방출을 유발하는 최종 돌연변이로부터 원래 배열을 보존하는 역할을합니다.

const array = ['9', '91', '95', '96', '99'];
const x = array
    .slice(0)                         // create copy of "array" for iterating
    .reduce((acc, curr, i, arr) => {
       if (i === 2) arr.splice(1);    // eject early by mutating iterated copy
       return (acc += curr);
    }, '');

console.log("x: ", x, "\noriginal Arr: ", array);
// x:  99195
// original Arr:  [ '9', '91', '95', '96', '99' ]


낡은

reduce 함수의 네 번째 인수 인 "array"를 변경하여 .reduce () 호출의 반복을 중단 할 수 있습니다. 사용자 정의 감소 기능이 필요하지 않습니다. 전체 매개 변수 목록은 문서 를 참조하세요 .reduce().

Array.prototype.reduce ((acc, curr, i, array))

네 번째 인수는 반복되는 배열 입니다.

const array = ['9', '91', '95', '96', '99'];
const x = array
.reduce((acc, curr, i, arr) => {
    if(i === 2) arr.splice(1);  // eject early
    return acc += curr;
  }, '');
console.log('x: ', x);  // x:  99195

왜?:

제시된 다른 많은 솔루션 대신 이것을 사용하는 유일한 이유는 알고리즘에 대한 함수형 프로그래밍 방법론을 유지하고이를 달성하기 위해 가능한 가장 선언적인 접근 방식을 원하는 경우입니다. 전체 목표가 문자 그대로 배열을 거짓이 아닌 대체 기본 형식 (문자열, 숫자, 부울, 기호)으로 줄이는 것이라면 이것이 실제로 가장 좋은 방법이라고 주장합니다.

왜 안돼?

그것은 나쁜 습관이기 때문에 함수 매개 변수를 변경하지 않기 위해 만드는 전체 인수 목록이 있습니다.


3
+1. 이것은 받아 들여진 대답이어야합니다. 그러나이 솔루션은 "WHY NOT"에 명시된 이유로 사용해서는 안됩니다.
johndodo

3
splice눈에 보이는 돌연변이 ( array)를 수행 하기 때문에 이것은 정말 나쁜 조언 입니다. 기능적 패러다임에 따르면 연속 전달 스타일의 감소를 사용하거나 오른쪽 연관 감소로 지연 평가를 활용합니다. 또는 더 간단한 대안으로 평범한 재귀입니다.

기다려! reduce 함수의 네 번째 인수를 변경하여 : "array" 는 올바른 문장이 아닙니다. 이 경우에는 배열을 단일 길이 배열 (첫 번째 요소)로 자르고 이미 인덱스 2에 도달했기 때문에 발생합니다 (답변의 예) , 분명히 다음 번에 인덱스 3에 대해 반복 할 항목을 얻지 못할 것입니다. 길이 1 )의 배열에 대한 원래 참조를 변경하고 있습니다. 소스 배열도 변경하지만 그 사이에서 멈추지 않는 팝을 수행하는 경우 (두 번째 마지막 인덱스에 있지 않은 경우).
Koushik Chatterjee

@KoushikChatterjee 내 진술은 내 암시 적 의미에 맞습니다. 당신의 명시적인 의미에 맞지 않습니다. 귀하의 요점을 포함하도록 진술 수정에 대한 제안을 제공해야하며 전반적인 답변을 개선 할 수 있도록 수정하겠습니다.
도비야 렉스

1
I는 ... 배열] .reduce (), 원치 않는 돌연변이를 피하기 확산 연산자 도달 선호
eballeste

16

감소를 사용하지 마십시오. 일반 반복자 (for 등)로 배열을 반복하고 조건이 충족되면 중단하십시오.


58
이것의 재미는 어디입니까? :)
Alexander Mills

2
@AlexanderMills 아마도 그는 제국주의자가되는 것을 좋아할 것입니다!
dimpiax

3
이 답변은 여기에 0 값이 있습니다
fedeghe

왜 이것이 많은 찬성표를 얻었는지 모르겠습니다. OP가 reduce ()에서 일찍 휴식을 취하는 방법을 물었 기 때문에 이것은 대답이 아닙니다. 몸을 굽히지 마십시오.
ricosrealm

12

반환 값에 신경 쓰지 않는 한 someevery 와 같은 함수를 사용할 수 있습니다 . 모든 콜백 false를 반환 휴식, 일부 사실 반환 할 때 :

things.every(function(v, i, o) {
  // do stuff 
  if (timeToBreak) {
    return false;
  } else {
    return true;
  }
}, thisArg);

25
그가 무엇을하려고한다면 reduce정의에 의해 그는 않습니다 반환 값을 걱정.

1
@ torazaburo— 확실하지만 OP에서 사용되는 것을 보지 못하며 결과를 얻는 다른 방법이 있습니다. ;-)
RobG

6

물론의 내장 버전을 reduce조기에 종료 할 수있는 방법은 없습니다 .

그러나 특별한 토큰을 사용하여 루프가 중단되어야하는시기를 식별하는 자체 버전의 reduce를 작성할 수 있습니다.

var EXIT_REDUCE = {};

function reduce(a, f, result) {
  for (let i = 0; i < a.length; i++) {
    let val = f(result, a[i], i, a);
    if (val === EXIT_REDUCE) break;
    result = val;
  }
  return result;
}

다음과 같이 사용하여 배열을 합산하지만 99를 누르면 종료됩니다.

reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0);

> 3

1
지연 평가 또는 CPS 를 사용 하여 원하는 동작을 얻을 수 있습니다.
scriptum

이 답변의 첫 번째 문장이 잘못되었습니다. 중단 할 수 있습니다. 자세한 내용은 아래 내 대답을 참조하십시오.
Tobiah Rex

4

Array.every는 고차 반복에서 벗어나는 매우 자연스러운 메커니즘을 제공 할 수 있습니다.

const product = function(array) {
    let accumulator = 1;
    array.every( factor => {
        accumulator *= factor;
        return !!factor;
    });
    return accumulator;
}
console.log(product([2,2,2,0,2,2]));
// 0


1

예외를 발생시켜 모든 코드 (따라서 반복기의 모든 빌드)를 중단 할 수 있습니다.

function breakReduceException(value) {
    this.value = value
}

try {
    Things.reduce(function(memo, current) {
        ...
        if (current <= 0) throw new breakReduceException(memo)
        ...
    }, 0)
} catch (e) {
    if (e instanceof breakReduceException) var memo = e.value
    else throw e
}

6
이것은 아마도 모든 답변 중에서 가장 효율적이지 않은 실행 일 것입니다. Try / catch는 기존 실행 컨텍스트를 중단하고 실행의 '느린 경로'로 돌아갑니다. V8이 내부적으로 수행하는 모든 최적화에 작별 인사를하십시오.
Evan Plaice 2018

5
충분히 극단적이지 않습니다. 이건 어때 :if (current <= 0) window.top.close()
user56reinstatemonica8

0

는 AS promise의 가지고 resolvereject콜백 인수를, 내가 만든 reduce과 해결 기능 break콜백 인수를. reduce첫 번째 인수 가 작업 할 배열이라는 점을 제외하면 네이티브 메서드 와 동일한 인수를 모두 사용합니다 (원숭이 패치 방지). 세 번째 [2] initialValue인수는 선택 사항입니다. function감속기에 대해서는 아래 스 니펫을 참조하십시오 .

var list = ["w","o","r","l","d"," ","p","i","e","r","o","g","i"];

var result = reducer(list,(total,current,index,arr,stop)=>{
  if(current === " ") stop(); //when called, the loop breaks
  return total + current;
},'hello ');

console.log(result); //hello world

function reducer(arr, callback, initial) {
  var hasInitial = arguments.length >= 3;
  var total = hasInitial ? initial : arr[0];
  var breakNow = false;
  for (var i = hasInitial ? 0 : 1; i < arr.length; i++) {
    var currentValue = arr[i];
    var currentIndex = i;
    var newTotal = callback(total, currentValue, currentIndex, arr, () => breakNow = true);
    if (breakNow) break;
    total = newTotal;
  }
  return total;
}

다음은 reducer배열 method수정 스크립트입니다.

Array.prototype.reducer = function(callback,initial){
  var hasInitial = arguments.length >= 2;
  var total = hasInitial ? initial : this[0];
  var breakNow = false;
  for (var i = hasInitial ? 0 : 1; i < this.length; i++) {
    var currentValue = this[i];
    var currentIndex = i;
    var newTotal = callback(total, currentValue, currentIndex, this, () => breakNow = true);
    if (breakNow) break;
    total = newTotal;
  }
  return total;
};

var list = ["w","o","r","l","d"," ","p","i","e","r","o","g","i"];

var result = list.reducer((total,current,index,arr,stop)=>{
  if(current === " ") stop(); //when called, the loop breaks
  return total + current;
},'hello ');


console.log(result);

0

중단 기능이있는 축소 버전은 '변환'으로 구현할 수 있습니다. 밑줄로.

구현 감소가 현재 사용중인 데이터 구조를 변경할 필요가 없도록 구성 플래그를 사용하여 구현하려고했습니다.

const transform = (arr, reduce, init, config = {}) => {
  const result = arr.reduce((acc, item, i, arr) => {
    if (acc.found) return acc

    acc.value = reduce(config, acc.value, item, i, arr)

    if (config.stop) {
      acc.found = true
    }

    return acc
  }, { value: init, found: false })

  return result.value
}

module.exports = transform

사용법 1, 간단한 것

const a = [0, 1, 1, 3, 1]

console.log(transform(a, (config, acc, v) => {
  if (v === 3) { config.stop = true }
  if (v === 1) return ++acc
  return acc
}, 0))

사용법 2, config를 내부 변수로 사용

const pixes = Array(size).fill(0)
const pixProcessed = pixes.map((_, pixId) => {
  return transform(pics, (config, _, pic) => {
    if (pic[pixId] !== '2') config.stop = true 
    return pic[pixId]
  }, '0')
})

Usage3, 구성을 외부 변수로 캡처

const thrusts2 = permute([9, 8, 7, 6, 5]).map(signals => {
  const datas = new Array(5).fill(_data())
  const ps = new Array(5).fill(0)

  let thrust = 0, config
  do {

    config = {}
    thrust = transform(signals, (_config, acc, signal, i) => {
      const res = intcode(
        datas[i], signal,
        { once: true, i: ps[i], prev: acc }
      )

      if (res) {
        [ps[i], acc] = res 
      } else {
        _config.stop = true
      }

      return acc
    }, thrust, config)

  } while (!config.stop)

  return thrust
}, 0)

0

reduce메서드 내부에서 중단 할 수 없습니다 . 달성하려는 작업에 따라 최종 결과를 변경할 수 있습니다 (이것이이를 수행하려는 이유 중 하나입니다).

const result = [1, 1, 1].reduce((a, b) => a + b, 0); // returns 3

console.log(result);

const result = [1, 1, 1].reduce((a, b, c, d) => {
  if (c === 1 && b < 3) {
    return a + b + 1;
  } 
  return a + b;
}, 0); // now returns 4

console.log(result);

유의 사항 : 배열 매개 변수를 직접 재 할당 할 수 없습니다.

const result = [1, 1, 1].reduce( (a, b, c, d) => {
  if (c === 0) {
    d = [1, 1, 2];
  } 
  return a + b;
}, 0); // still returns 3

console.log(result);

그러나 (아래에서 지적했듯이) 배열의 내용을 변경하여 결과에 영향을 미칠 수 있습니다.

const result = [1, 1, 1].reduce( (a, b, c, d) => {
  if (c === 0) {
    d[2] = 100;
  } 
  return a + b;
}, 0); // now returns 102

console.log(result);


1
Re " 후속 계산에 영향을 미치는 방식으로 인수 값을 직접 변경할 수 는 없습니다. ECMA-262는 다음과 같이 말합니다. 배열의 기존 요소가 변경되면 callbackfn에 전달 된 값이 방문 횟수를 줄일 때의 값이됩니다 . 원래 배열을 수정하지 않고 d에 새 값을 할당하기 때문에 예제가 작동하지 않습니다 . 교체 d = [1, 1, 2]d[2] = 6어떻게되는지. ;-)
RobG

-1

동일한 문제를 해결하면서 얻은 또 다른 간단한 구현 :

function reduce(array, reducer, first) {
  let result = first || array.shift()

  while (array.length > 0) {
    result = reducer(result, array.shift())
    if (result && result.reduced) {
      return result.reduced
    }
  }

  return result
}

-1

아래 패턴을 사용하여 reduce를 사용하여 순차적으로 promise를 연결하려는 경우 :

return [1,2,3,4].reduce(function(promise,n,i,arr){
   return promise.then(function(){
       // this code is executed when the reduce loop is terminated,
       // so truncating arr here or in the call below does not works
       return somethingReturningAPromise(n);
   });
}, Promise.resolve());

그러나 약속 안팎에서 일어나는 일에 따라 중단해야 할 필요가 있습니다. 첫 번째 약속이 실행되기 전에 reduce 루프가 종료되어 약속 콜백에서 배열을 자르는 것이 쓸모 없기 때문에 일이 조금 더 복잡해집니다.이 구현으로 끝났습니다.

function reduce(array, promise, fn, i) {
  i=i||0;
  return promise
  .then(function(){
    return fn(promise,array[i]);
  })
  .then(function(result){
    if (!promise.break && ++i<array.length) {
      return reduce(array,promise,fn,i);
    } else {
      return result;
    }
  })
}

그런 다음 다음과 같이 할 수 있습니다.

var promise=Promise.resolve();
reduce([1,2,3,4],promise,function(promise,val){
  return iter(promise, val);
}).catch(console.error);

function iter(promise, val) {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      if (promise.break) return reject('break');
      console.log(val);
      if (val==3) {promise.break=true;}
      resolve(val);
    }, 4000-1000*val);
  });
}

-1

예를 들어 some단락이 많이 절약 할 수 있는 방법에서 다음과 같이 해결했습니다 .

const someShort = (list, fn) => {
  let t;
  try {
    return list.reduce((acc, el) => {
      t = fn(el);
      console.log('found ?', el, t)
      if (t) {
        throw ''
      }
      return t
    }, false)
  } catch (e) {
    return t
  }
}

const someEven = someShort([1, 2, 3, 1, 5], el => el % 2 === 0)

console.log(someEven)

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