对象上的 Javascript reduce()

Posted

技术标签:

【中文标题】对象上的 Javascript reduce()【英文标题】:Javascript reduce() on Object 【发布时间】:2013-03-22 20:02:33 【问题描述】:

有一个很好的数组方法reduce() 可以从数组中获取一个值。示例:

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

用对象实现相同目标的最佳方法是什么?我想这样做:

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

但是,Object 似乎没有实现任何reduce() 方法。

【问题讨论】:

你在用Underscore.js吗? 不。 Underscore 是否为对象提供 reduce 功能? 我不记得了。我知道它有一个reduce 方法。我会在那里检查。不过,解决方案似乎并不难。 @Sethen Maleno,@Pavel:是的,_ 确实有对象的归约。不确定它是否是偶然的,或者对象支持是否是故意的,但确实你可以像这个问题的例子一样传递一个对象,它会(从概念上)for..in,用每个键找到的值调用你的迭代器函数。 【参考方案1】:

一种选择是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);

有了这个,您需要指定一个初始值,否则第一轮将是'a' + 2

如果您希望将结果作为对象 ( value: ... ),则每次都必须初始化并返回该对象:

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

【讨论】:

不错的答案,但使用 Object.values 而不是 Object.keys 更具可读性,因为我们关心的是这里的值而不是键。应该是这样的: Object.values(o).reduce((total, current) => total + current.value, 0); Object.values 的浏览器支持比 Object.keys 差得多,但如果您使用 polyfill 或使用 Babel 进行转译,这可能不是问题 确切地说,我在从 mongo 模型中检索到的甜菜键中使用它,并且我将一个空对象作为初始值传递给键的减少结果,就像我使用 @ 987654328@插件,我被传播了累加器值以换取reduce,它就像一个魅力,但我想知道我是否做错了什么,因为我将对象传递给reduce的初始值,你的回答证明了我我做对了! 但是使用 Object.values 并不能让您访问正在使用的实际密钥。使用 key ,特别是在归约中,是很常见的 还有——除了示例之外——谁有他们想要简单总结的对象属性列表?大多数时候对象属性是不同的。【参考方案2】:

您可以使用生成器表达式(多年来在所有浏览器中以及在 Node 中都支持)来获取可以减少的列表中的键值对:

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

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

【讨论】:

【参考方案3】:

首先,你不太明白 reduce 之前的值是什么。

在您的伪代码中,您有return previous.value + current.value,因此previous 值将是下一次调用时的数字,而不是对象。

其次,reduce 是一个 Array 方法,而不是 Object 的方法,并且在迭代对象的属性时不能依赖顺序(参见:https://developer.mozilla.org/en-US/docs/javascript/Reference/Statements/for...in,这适用于 @987654323 @ 也);所以我不确定在对象上应用reduce 是否有意义。

但是,如果顺序不重要,您可以:

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

或者你也可以map对象的值:

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

附:在 ES6 中使用胖箭头函数的语法(已经在 Firefox Nightly 中),你可以缩小一点:

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

【讨论】:

【参考方案4】:

如果你可以使用数组,请使用数组,数组的长度和顺序是它的一半。

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

/* 返回值:(数字) 6 */

【讨论】:

【参考方案5】:

这不是很难自己实现:

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;

在行动:

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

您也可以将其添加到 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);
    

【讨论】:

【参考方案6】:

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

使用示例。

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 it,伙计们! ;-)

【讨论】:

-1,现在您在所有未来对象上都有一个新的可枚举属性:jsfiddle.net/ygonjooh Don't modify objects you don't own. 请不要这样修改基础原型。这可能会给未来使用相同代码库的开发人员带来很多问题 是的,这是一个“猴子补丁”这个解决方案是6年前写的,现在不太相关,请记住,例如,在2021年使用Object.entries()会更好 【参考方案7】:

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

【讨论】:

【参考方案8】:

ES6 实现: 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.value;
, 0);

【讨论】:

Object.entries(o); // returns [['value',1],['value',2],['value',3]] const [键,值] = 对;我从来没有见过这个! @martin-meeser - 这称为解构。我们甚至可以通过将function (total, pair) 更改为function (total, [key, value]) 来省略这一行 @faboulaws Object.entries(o); // 返回 [["a", value: 1 ], ["b", value: 2 ], ["c", value: 3 ]] @faboulaws 你的回答错了,最后一行应该是return total + value.value。因为 Object.entries(o) [ [ "a", "value": 1 ], [ "b", "value": 2 ], [ "c", "value": 3 ] ] ,这是非常具有误导性的。甚至有 3 个人竖起大拇指...【参考方案9】:

在这种情况下,您真正​​想要的是Object.values。考虑到这一点,这是一个简洁的 ES6 实现:

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

或者简单地说:

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


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

console.log(total) // 6

【讨论】:

这是您链接到的 Array.prototype.values() - 现在已编辑 不会reduce把默认值作为第二个参数吗?【参考方案10】:

由于尚未真正在答案中得到确认,Underscore 的reduce 也适用于此。

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

注意,如果列表对象只有一项,_.reduce 将返回唯一的值(对象或其他),而不调用迭代器函数。

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

//returns value: 1 instead of 1

【讨论】:

你是说lodash? 不,我的意思是下划线。但两者都有效。他们都有reduce() 哎呀,我从来没有听说过下划线谢谢。【参考方案11】:

一个对象可以变成一个数组:Object.entries(),Object.keys(),Object.values(),然后被归约为数组。但是您也可以在不创建中间数组的情况下减少对象。

我创建了一个小助手库odict 用于处理对象。

npm install --save odict

它具有reduce 功能,非常类似于Array.prototype.reduce():

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

您也可以将其分配给:

Object.reduce = reduce;

因为这个方法非常有用!

所以你的问题的答案是:

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

【讨论】:

【参考方案12】:

试试这个单线箭头功能

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

【讨论】:

“试试这个”的答案在 Stack Overflow 上的价值很低,因为他们错过了赋能/教育成千上万未来研究人员的机会。【参考方案13】:

试试这个。它将从其他变量中对数字进行排序。

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)

【讨论】:

【参考方案14】:

如果作为数组处理会容易得多

返回水果的总量:

let fruits = [ name: 'banana', id: 0, quantity: 9 ,  name: 'strawberry', id: 1, quantity: 1 ,  name: 'kiwi', id: 2, quantity: 2 ,  name: 'apple', id: 3, quantity: 4 ]

let total = fruits.reduce((sum, f) => sum + f.quantity, 0);

【讨论】:

【参考方案15】:

让我总结一下可能性。目标始终是从对象中创建一个数组。为此有各种 Javascript 对象函数。对于每个单独的函数,都有不同的解释方式。所以它总是取决于我们的对象是什么样子以及我们想要做什么。

在上面的例子中,它是一个包含三个对象的对象。

const obj =  
    a: value: 1, 
    b: value: 2, 
    c: value:3 
;

使用 Object.keys

Object.keys 只给我们对象的键。

const arr = Object.keys(obj);
// output arr: 
[a, b, c]

const result = arr.reduce((total, key) => 
    return sum + obj[key].value;
, 0);
// output result
// 6

使用 Object.value

Object.value() 返回数组中的每一个值。

const arr = Object.value(obj);
// output arr
[
   value: 1,
   value: 2,
   value: 3,
]

const result = arr.reduce((total, singleValue) => 
   return total + singleValue.value;
, 0);

// output result
// 6

// Or the short variant
const resultShort = Object.values(obj).reduce((t, n) => t + n.value, 0)

// output resultShort
// 6

使用 Object.entries

Object.entries 将每个单独的对象值拆分为一个数组。

const arr = Object.entries(obj)
// output arr
[
  ["a", visitors: 1],
  ["b", visitors: 2],
  ["c", visitors: 4]
]

const result = arr.reduce((total, singleArr) => 
  return total + singleArr[1].value;
, 0);

// output result
// 6

你是使用reduce还是使用数组函数map()取决于你和你想要做什么。

【讨论】:

以上是关于对象上的 Javascript reduce()的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 基于reduce方法实现数组内对象的同属性运算

如何使用 reduce 函数 javascript 对对象进行分组

javascript 使用Array reduce可以安全地访问嵌套对象。

Javascript 中使用 Array.reduce 的相同对象的总和

从给数组中的对象去重看Javascript中的reduce()

如何使用 reduce 测试 Math.max 函数的数组