2013-04-01 15 views
84

C'è un buon metodo di matrice reduce() per ottenere un valore dall'array. Esempio:Javascript reduce() sull'oggetto

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

Qual è il modo migliore per ottenere lo stesso con gli oggetti? Mi piacerebbe fare questo:

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

Tuttavia, oggetto non sembra avere alcun metodo reduce() implementato.

+1

Si sta utilizzando 'Underscore.js'? – Sethen

+0

No. Underscore fornisce una riduzione per gli oggetti? –

+0

Non ricordo. So che ha un metodo 'reduce'. Vorrei controllare lì. Però, la soluzione non sembra così difficile. – Sethen

risposta

171

Una possibilità potrebbe essere quella di reduce il keys():

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

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

Con questo, ti consigliamo di specificare un valore iniziale o il 1 ° turno sarà 'a' + 2.

Se si desidera che il risultato come un oggetto ({ value: ... }), dovrete inizializzare e restituire l'oggetto ogni volta:

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

Risposta piacevole ma è più leggibile utilizzare Object.values ​​invece di Object.keys perché siamo preoccupati dei valori qui non delle chiavi. Dovrebbe essere così: Object.values ​​(o) .reduce ((total, current) => total + current.value, 0); –

+0

Oggetto.i valori hanno un supporto browser peggiore rispetto a Object.keys, ma questo potrebbe non essere un problema se si utilizza un polyfill o si esegue il transpile con Babel – duhaime

1

è possibile utilizzare un generatore di espressione (supportato in tutti i browser ormai da anni , e in Node) per ottenere le coppie chiave-valore in un elenco è possibile ridurre il:

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

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

Prima di tutto, non abbastanza ottenere ciò che è reduce 's valore precedente viene.

Nello pseudo codice si ha return previous.value + current.value, pertanto il valore previous sarà un numero alla successiva chiamata, non un oggetto.

In secondo luogo, reduce è un metodo Array, non uno di un oggetto, e non si può contare su l'ordine quando si sta iterazione le proprietà di un oggetto (vedi: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for...in, questo viene applicato a Object.keys troppo); quindi non sono sicuro che l'applicazione di reduce su un oggetto abbia senso.

Tuttavia, se l'ordine non è importante, è possibile avere:

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

oppure si può semplicemente map valore dell'oggetto:

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

P.S. in ES6 con la sintassi della funzione freccia grassi (già in Firefox Nightly), si potrebbe ridurre un po ':

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

Se è possibile utilizzare un array, si usa un array, la lunghezza e l'ordine di un array sono la metà della sua di valore.

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

/* valore restituito: (Number) */

2

Questo non è molto difficile da attuare da soli:

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

In azione:

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 

Puoi anche aggiungerlo al prototipo Object:

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

Estendere Object.prototype.

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

Esempio di utilizzo.

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'Joy, ragazzi !! ;-)

+0

-1, ora si ha una nuova proprietà enumerabile su tutti gli oggetti futuri: http: // jsfiddle. net/ygonjooh/ – Johan

+1

"hasOwnProperty()" aiuterà U. http://jsfiddle.net/rain/ygonjooh/1/ – user1247458

2

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

ES6 attuazione: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); 
+2

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

+0

const [chiave, valore] = coppia; Non l'ho mai visto! –

+0

@ martin-meeser: si chiama destrutturazione. Possiamo anche omettere questa riga cambiando la funzione '' (totale, coppia) 'a' (totale, [chiave, valore]) ' –

7

Ciò che in questo caso si desidera è lo Object.values. Ecco una concisa implementazione ES6 con questo in mente:

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

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

console.log(total) // 6 

o semplicemente:

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

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

console.log(total) // 6 
+0

Questo è Array.prototype.values ​​() a cui sei collegato - modificato ora –

+0

Ah buona cattura. .. –

0

Poiché eccedente davvero stata confermata in una risposta ancora, di reduce Underscore funziona anche per questo.

_.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 
}) 

nota, _.reduce restituirà l'unico valore (oggetto o altro) se l'oggetto lista ha un solo elemento, senza chiamare la funzione iteratore.

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

//returns {value: 1} instead of 1 
Problemi correlati