Nesne üzerindeki Javascript reduce ()


182

Diziden reduce()bir değer elde etmek için güzel Array yöntemi vardır . Misal:

[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
  return previousValue + currentValue;
});

Nesnelerle aynı şeyi yapmanın en iyi yolu nedir? Bunu yapmak istiyorum:

{ 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
}.reduce(function(previous, current, index, array){
  return previous.value + current.value;
});

Ancak, Object herhangi bir reduce()yöntem uygulanmış görünmüyor .


1
Kullanıyor musunuz Underscore.js?
Sethen

Hayır! Alt çizgi nesneler için azaltma sağlar mı?
Pavel

Hatırlayamıyorum. Bunun bir reduceyöntemi olduğunu biliyorum . Ben orada kontrol ediyorum. Yine de, çözüm o kadar zor görünmüyor.
Sethen

1
@Sethen Maleno, @Pavel: evet _nesneler için bir azalma var. Kazayla çalışıp çalışmadığından veya nesne desteğinin kasıtlı olup olmadığından emin değilsiniz, ancak gerçekten bu sorunun örneğindeki gibi bir nesneyi iletebilirsiniz ve (kavramsal olarak) for..in, her anahtarda bulunan değerlerle yineleyici işlevinizi çağıracaktır.
Roatin Marth

Yanıtlar:


55

Bu durumda aslında istediğiniz şey Object.values. İşte bunu göz önünde bulundurarak kısa bir ES6 uygulaması:

const add = {
  a: {value:1},
  b: {value:2},
  c: {value:3}
}

const total = Object.values(add).reduce((t, {value}) => t + value, 0)

console.log(total) // 6

ya da sadece:

const add = {
  a: 1,
  b: 2,
  c: 3
}

const total = Object.values(add).reduce((t, n) => t + n)

console.log(total) // 6

Bağlantı kurduğunuz Array.prototype.values ​​() - şimdi düzenlendi
Jonathan Wood

281

Seçeneklerden biri olacaktır :reducekeys()

var o = { 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
};

Object.keys(o).reduce(function (previous, key) {
    return previous + o[key].value;
}, 0);

Bununla, bir başlangıç ​​değeri belirtmek isteyeceksiniz veya ilk tur 'a' + 2 .

Sonucu bir Object ( { value: ... }) olarak istiyorsanız, nesneyi her seferinde başlatmanız ve geri göndermeniz gerekir:

Object.keys(o).reduce(function (previous, key) {
    previous.value += o[key].value;
    return previous;
}, { value: 0 });

15
Güzel cevap ama Object.keys yerine Object.values ​​kullanmak daha okunabilir çünkü biz burada anahtarlarla değil değerler hakkında endişeliyiz. Şöyle olmalıdır: Object.values ​​(o) .reduce ((total, current) => total + current.value, 0);
Mina Luke

3
Object.values, Object.keys'den çok daha kötü tarayıcı desteğine sahiptir, ancak bir çoklu dolgu kullanırsanız veya Babel ile aktarıyorsanız, bu sorun olmayabilir
duhaime

tam olarak, bunu mongo modelinden alınan pancar olan anahtarlarda kullanıyordum ve anahtarların azaltılması sonucuna ilk değer olarak boş bir nesneyi geçtim ve @babel/plugin-proposal-object-rest-spreadeklentiyi kullandığım kadar , akümülatör değerini yaydım iadesi azaltmak ve bir cazibe gibi çalışır, ama ben yanlış bir şey yapıyor, ben obejct azaltma ilk değerine geçiyordu neden olur merak ediyorum ve cevabınız ben doğru şeyi yaptım bana kanıtladı!
a_m_dev

55

ES6 uygulaması: Object.entries ()

const o = {
  a: {value: 1},
  b: {value: 2},
  c: {value: 3}
};

const total = Object.entries(o).reduce(function (total, pair) {
  const [key, value] = pair;
  return total + value;
}, 0);

3
Object.entries(o); // returns [['value',1],['value',2],['value',3]]
faboulaws

1
const [anahtar, değer] = çift; Bunu hiç görmedim!
Martin Meeser

9
@ martin-meeser - buna yıkım denir. Hatta değiştirerek bu çizgiyi atlayabilirsiniz function (total, pair)içinfunction (total, [key, value])
Jakub Zawiślak

Bunu entriesyapmanın daha temiz bir yolu olsa keysda, daha az performans gösterir. hackernoon.com/…
robdonn

@faboulaws Object.entries (o); // [["a", {değer: 1}], ["b", {değer: 2}], ["c", {değer: 3}]]
Thomas

19

Her şeyden önce, azaltmanın önceki değerinin ne olduğunu tam olarak elde edemezsiniz .

Sahte kodunuzda return previous.value + current.value, bu nedenle previousdeğer bir nesne değil, bir sonraki çağrıda bir numara olacaktır.

İkincisi, reducebir Nesne yöntemi değil, bir Array yöntemidir ve bir nesnenin özelliklerini tekrarlarken sıraya güvenemezsiniz (bkz: https://developer.mozilla.org/en-US/docs/ JavaScript / Reference / Statements / for ... in , bu Object.keys için de geçerlidir); bu yüzden uygulayıp uygulamadığından emin değilimreduce bir nesneye mantıklı .

Ancak, sipariş önemli değilse, sahip olabilirsiniz:

Object.keys(obj).reduce(function(sum, key) {
    return sum + obj[key].value;
}, 0);

Veya sadece nesnenin değerini eşleyebilirsiniz :

Object.keys(obj).map(function(key) { return this[key].value }, obj).reduce(function (previous, current) {
    return previous + current;
});

Yağ ok işlevinin sözdizimiyle ES6'da PS (zaten Firefox Nightly'de), biraz küçültebilirsiniz:

Object.keys(obj).map(key => obj[key].value).reduce((previous, current) => previous + current);

3

1:

[{value:5}, {value:10}].reduce((previousValue, currentValue) => { return {value: previousValue.value + currentValue.value}})

>> Object {value: 15}

2:

[{value:5}, {value:10}].map(item => item.value).reduce((previousValue, currentValue) => {return previousValue + currentValue })

>> 15

3:

[{value:5}, {value:10}].reduce(function (previousValue, currentValue) {
      return {value: previousValue.value + currentValue.value};
})

>> Object {value: 15}

2

Object.prototype öğesini genişletin.

Object.prototype.reduce = function( reduceCallback, initialValue ) {
    var obj = this, keys = Object.keys( obj );

    return keys.reduce( function( prevVal, item, idx, arr ) {
        return reduceCallback( prevVal, item, obj[item], obj );
    }, initialValue );
};

Kullanım örneği.

var dataset = {
    key1 : 'value1',
    key2 : 'value2',
    key3 : 'value3'
};

function reduceFn( prevVal, key, val, obj ) {
    return prevVal + key + ' : ' + val + '; ';
}

console.log( dataset.reduce( reduceFn, 'initialValue' ) );
'Output' == 'initialValue; key1 : value1; key2 : value2; key3 : value3; '.

n'Oyun, çocuklar !! ;-)


-1, şimdi gelecekteki tüm nesnelerde yeni bir numaralandırılabilir mülkünüz var: jsfiddle.net/ygonjooh
Johan


1
Lütfen temel prototipi böyle değiştirmeyin. Bu, aynı kod tabanında çalışan gelecekteki geliştiriciler için birçok soruna yol açabilir
adam.k

Evet, bu bir "maymun yama" Bu çözüm 6 yıl önce yazılmıştır ve şimdi çok ilgili değil, sadece aklınızda bulundurun Ve örneğin, Object.entries()2021'de kullanmak daha iyi olacak
user1247458

2

Aşağıdakileri azaltabileceğiniz bir listede anahtar / değer çiftlerini almak için bir jeneratör ifadesini (yıllardır tüm tarayıcılarda ve Düğümde desteklenir) kullanabilirsiniz:

>>> a = {"b": 3}
Object { b=3}

>>> [[i, a[i]] for (i in a) if (a.hasOwnProperty(i))]
[["b", 3]]

2

Bir nesne şununla bir diziye dönüştürülebilir: Object.entries () , Object.keys () , Object.values ​​() ve sonra dizi olarak azaltılabilir. Ancak bir nesneyi ara dizi oluşturmadan da azaltabilirsiniz.

Nesnelerle çalışmak için küçük bir yardımcı kütüphane yarattım .

npm install --save odict

Array.prototype.reduce ()reduce gibi çok çalışan bir işleve sahiptir :

export const reduce = (dict, reducer, accumulator) => {
  for (const key in dict)
    accumulator = reducer(accumulator, dict[key], key, dict);
  return accumulator;
};

Ayrıca aşağıdakilere atayabilirsiniz:

Object.reduce = reduce;

çünkü bu yöntem çok faydalı!

Yani sorunuzun cevabı:

const result = Object.reduce(
  {
    a: {value:1},
    b: {value:2},
    c: {value:3},
  },
  (accumulator, current) => (accumulator.value += current.value, accumulator), // reducer function must return accumulator
  {value: 0} // initial accumulator value
);

1

Bir dizi kullanabiliyorsanız, bir dizi kullanın, bir dizinin uzunluğu ve sırası değerinin yarısı kadardır.

function reducer(obj, fun, temp){
    if(typeof fun=== 'function'){
        if(temp== undefined) temp= '';
        for(var p in obj){
            if(obj.hasOwnProperty(p)){
                temp= fun(obj[p], temp, p, obj);
            }
        }
    }
    return temp;
}
var O={a:{value:1},b:{value:2},c:{value:3}}

reducer(O, function(a, b){return a.value+b;},0);

/ * döndürülen değer: (Sayı) 6 * /


1

Kendinizi uygulamak çok zor değil:

function reduceObj(obj, callback, initial) {
    "use strict";
    var key, lastvalue, firstIteration = true;
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function');
    }   
    if (arguments.length > 2) {
        // initial value set
        firstIteration = false;
        lastvalue = initial;
    }
    for (key in obj) {
        if (!obj.hasOwnProperty(key)) continue;
        if (firstIteration)
            firstIteration = false;
            lastvalue = obj[key];
            continue;
        }
        lastvalue = callback(lastvalue, obj[key], key, obj);
    }
    if (firstIteration) {
        throw new TypeError('Reduce of empty object with no initial value');
    }
    return lastvalue;
}

Eylemde:

var o = {a: {value:1}, b: {value:2}, c: {value:3}};
reduceObj(o, function(prev, curr) { prev.value += cur.value; return prev;}, {value:0});
reduceObj(o, function(prev, curr) { return {value: prev.value + curr.value};});
// both == { value: 6 };

reduceObj(o, function(prev, curr) { return prev + curr.value; }, 0);
// == 6

Nesne prototipine de ekleyebilirsiniz:

if (typeof Object.prototype.reduce !== 'function') {
    Object.prototype.reduce = function(callback, initial) {
        "use strict";
        var args = Array.prototype.slice(arguments);
        args.unshift(this);
        return reduceObj.apply(null, args);
    }
}

0

Henüz bir cevapta doğrulanmadığı için Underscore reduceda bunun için çalışıyor.

_.reduce({ 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
}, function(prev, current){
    //prev is either first object or total value
    var total = prev.value || prev

    return total + current.value
})

_.reduceListe nesnesinin yineleyici işlevini çağırmadan yalnızca bir öğesi varsa, tek değeri (nesne veya başka bir şekilde) döndüreceğini unutmayın .

_.reduce({ 
    a: {value:1} 
}, function(prev, current){
    //not called
})

//returns {value: 1} instead of 1

"Bu diğer kütüphaneyi deneyin" yararlı değil
Grunion Shaftoe

Sizin için yararlı değil
Chris Dolphin

0

Bu tek astarlı ok işlevini deneyin

Object.values(o).map(a => a.value, o).reduce((ac, key, index, arr) => ac+=key)

0

Bunu dene. Diğer değişkenlerden sayıları sıralar.

const obj = {
   a: 1,
   b: 2,
   c: 3
};
const result = Object.keys(obj)
.reduce((acc, rec) => typeof obj[rec] === "number" ? acc.concat([obj[rec]]) : acc, [])
.reduce((acc, rec) => acc + rec)
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.