Erken kesme azaltma () yöntemi nasıl yapılır?


94

reduce()Yöntemin yinelemesini nasıl bozabilirim ?

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)

currentYukarıdaki kodda ne var ? Bunların aynı şeyi nasıl yapabileceğini anlamıyorum. Her durumda erken gibi kırmak yöntem vardır some, every,find
elclanrs

someve everyboolean döndürün ve findtek bir kayıt döndürün , istediğim bir not oluşturmak için işlemleri çalıştırmak. currentcurrentValue'dur. referans
Julio Marins

Demek istediğim current, ilk kod parçasında ne var ?
elclanrs

güncellendi, yanıt için teşekkürler
Julio Marins

2
Cevap, erken reducedönemezsiniz, erken çıkan veya kendi yardımcınızı yaratan yerleşik işlevlerle veya lodash veya başka bir şey kullanarak başka bir yol bulmanız gerekecek. Yapmak istediğiniz şeyin tam bir örneğini yayınlayabilir misiniz?
elclanrs

Yanıtlar:


94

GÜNCELLEME

Yorumculardan bazıları, .reduce()mantığın içine erken girmek için orijinal dizinin mutasyona uğradığını iyi bir şekilde belirtiyor .

Bu nedenle, bir takip adımını çağırmadan önce a ekleyerek orijinal dizinin bir kopyasını vererek yanıtı biraz değiştirdim . NOT : Aynı görevi gerçekleştiren benzer operasyonlar (daha az açık) ve yayma operatörü ( biraz daha az performanslı ). Unutmayın, bunların tümü genel çalışma süresine + 1 * (O (1)) ek bir sabit doğrusal zaman faktörü ekler..slice(0).reduce()slice()[...array]

Kopya, orijinal diziyi yinelemeden çıkarılmaya neden olan nihai mutasyondan korumaya yarar.

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' ]


ESKİ

Herhangi bir .reduce () çağrısının yinelemesini, azaltma işlevinin 4. bağımsız değişkenini değiştirerek kırabilirsiniz: "dizi". Özel bir azaltma işlevine gerek yoktur. Parametrelerin tam listesi için Dokümanlar'a bakın .reduce().

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

4. argüman, üzerinde yinelenen dizidir .

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

NEDEN?:

Sunulan diğer birçok çözüm yerine bunu kullanmayı düşünebilmemin tek ve tek nedeni, algoritmanız için işlevsel bir programlama metodolojisi sürdürmek ve bunu başarmak için mümkün olan en açıklayıcı yaklaşımı istemektir. Tüm amacınız bir diziyi yanlış olmayan alternatif bir ilkele (dize, sayı, boole, Sembol) GERÇEKLEŞTİRMEKse, o zaman bu IS aslında en iyi yaklaşımdır.

NEDEN OLMASIN?

Kötü bir uygulama olduğu için işlev parametrelerinin DEĞİŞTİRİLMEMESİ için yapılacak bir dizi argüman var.


3
+1. Kabul edilen cevap bu olmalıdır. Yine de bu çözüm "NEDEN OLMAYACAK" altında belirtilen nedenlerden dolayı asla kullanılmamalıdır.
johndodo

3
Bu gerçekten KÖTÜ TAVSİYE, çünkü splicegörünür bir mutasyon ( array) gerçekleştiriyor. İşlevsel paradigmaya göre, devam eden geçiş stilinde bir azalma ya da sağ-ilişkisel bir azaltma ile tembel değerlendirme kullanacaksınız. Veya, daha basit bir alternatif olarak, sadece düz özyineleme.

Tut! azaltma işlevinin 4. argümanını değiştirerek: "dizi" doğru bir ifade değildir. Bu durumda gerçekleşiyor (cevaptaki örnek), çünkü diziyi tek uzunluklu diziye (birinci eleman) keserken, zaten dizin 2'ye ulaştığı halde , açıkçası, bir sonraki sefer, dizin 3 için yinelenecek bir öğe almayacak orijinal referansı uzunluk dizisine dönüştürüyorsunuz 1 ). Kaynak diziyi de değiştirecek, ancak aralarında durmayacak bir pop gerçekleştirirseniz (son ikinci dizinde değilseniz).
Koushik Chatterjee

@KoushikChatterjee İfadem örtük anlamıma göre doğrudur. Açık anlamınız için doğru değil. İfadeyi puanlarınızı içerecek şekilde değiştirme konusunda bir öneri sunmalısınız ve genel yanıtı iyileştireceği için düzenlemeyi yapacağım.
Toviyaoğulları Rex

1
İstenmeyen mutasyonları önlemek için yayılma operatörüne ulaşmayı tercih ederim, [... dizi] .reduce ()
eballeste

16

İndirgeme kullanma. Normal yineleyicilerle (for, vb.) Dizi üzerinde yineleyin ve koşulunuz karşılandığında çıkın.


58
bunun eğlencesi nerede? :)
Alexander Mills

2
@AlexanderMills muhtemelen bir imperator olmayı seviyor!
dimpiax

3
bu cevabın burada 0 değeri var
fedeghe

Bunun neden bu kadar olumlu oy aldığından emin değilim ... OP, indirgemeden nasıl erken ayrılacağını sorduğundan () bu bir yanıt değil. eğilmemelisin.
ricosrealm

12

Dönüş değerini umursamadığınız sürece some ve every gibi işlevleri kullanabilirsiniz . geri arama yanlış döndürdüğünde her kesinti, bazıları doğru döndürdüğünde:

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

25
Yapmaya çalışıyor Ama eğer reducetanım gereği o mu dönüş değeri önemsiyorum.

1
@ torazaburo - elbette, ama bunun OP'de kullanıldığını görmüyorum ve sonuç almanın başka yolları da var. ;-)
RobG

6

reduceZamanından önce çıkmak için yerleşik sürümünü almanın elbette bir yolu yoktur .

Ancak, döngünün ne zaman kırılması gerektiğini belirlemek için özel bir belirteç kullanan kendi azaltma sürümünüzü yazabilirsiniz.

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

Bir diziyi toplamak için bu şekilde kullanın ancak 99'a bastığınızda çıkın:

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

> 3

1
Sen kullanabilirsiniz tembel değerlendirme veya CPS : İstenilen davranışı elde etmek
Scriptum

Bu cevabın ilk cümlesi yanlıştır. Ayrılabilirsin, detaylar için aşağıdaki cevabıma bakınız.
Toviyaoğulları Rex

4

Array.every, yüksek dereceli yinelemeden kurtulmak için çok doğal bir mekanizma sağlayabilir.

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

Bir istisna atarak her kodu ve dolayısıyla yineleyicideki her derlemeyi kırabilirsiniz:

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
Bu, muhtemelen tüm cevaplar arasında uygulama açısından en az verimli olanıdır. Try / catch, mevcut yürütme bağlamını bozar ve yürütmenin 'yavaş yoluna' geri döner. V8'in kapak altında yaptığı tüm optimizasyonlara elveda deyin.
Evan Plaice

5
Yeterince aşırı değil. Buna ne dersin:if (current <= 0) window.top.close()
user56reinstatemonica8

0

As promiseler var resolvevereject geri arama argümanlar, benim yarattığım reduceile geçici çözüm işlevini breakgeri arama argüman. Yerel reduceyöntemle aynı argümanları alır , ancak ilki üzerinde çalışılacak bir dizi (maymun yamasından kaçının). Üçüncü [2] initialValueargüman isteğe bağlıdır. functionRedüktör için aşağıdaki parçacığa bakın .

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

Ve işte burada reducer bir Dizi olarak methoddeğiştirilmiş komut dosyası:

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

Fonksiyonel sürümü kesintili azaltma, örneğin 'dönüştürme' olarak uygulanabilir. alt çizgi olarak.

Bunu durdurmak için bir yapılandırma bayrağıyla uygulamaya çalıştım, böylece uygulama azaltma şu anda kullanmakta olduğunuz veri yapısını değiştirmek zorunda kalmaz.

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

Kullanım1, basit olan

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

Usage2, dahili değişken olarak config kullanın

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

Kullanım3, yapılandırmayı harici değişken olarak yakala

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

Bir reduceyöntemin içinden kopamazsınız. Neyi başarmaya çalıştığınıza bağlı olarak nihai sonucu değiştirebilirsiniz (bu, bunu yapmak isteyebileceğiniz bir nedendir)

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

Unutmayın: dizi parametresini doğrudan yeniden atayamazsınız

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

Ancak (aşağıda belirtildiği gibi), dizinin içeriğini değiştirerek sonucu etkileyebilirsiniz:

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
Yeniden " Bağımsız değişken değerlerini , sonraki hesaplamaları etkileyecek şekilde doğrudan değiştiremezsiniz ", bu doğru değil. ECMA-262 diyor ki: Dizinin mevcut öğeleri değiştirilirse, callbackfn'e aktarılan değerleri, ziyaretleri azaltma anındaki değer olacaktır . Örneğiniz, orijinal diziyi değiştirmeden, d'ye yeni bir değer atadığınız için çalışmıyor. Değiştir d = [1, 1, 2]ile d[2] = 6ve ne olduğunu görün. ;-)
RobG

-1

Aynı sorunu çözmek için geldiğim bir başka basit uygulama:

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

Aşağıdaki kalıbı kullanarak azaltarak taahhütleri sırayla zincirlemek istiyorsanız:

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());

Ancak, bir vaatin içinde veya dışında olan bir şeye göre kırılma ihtiyacı, işler biraz daha karmaşık hale gelir çünkü azaltma döngüsü ilk vaat yerine getirilmeden sonlandırılır, vaat edilen geri aramalardaki dizinin kesilmesi işe yaramaz hale gelir, bu uygulamayı yaptım:

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

O zaman bunun gibi bir şey yapabilirsiniz:

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

Bunu şu şekilde çözdüm, örneğin somekısa devrenin çok fazla tasarruf sağlayabileceği yöntemde:

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)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.