使用JavaScript对JSON对象进行Map和Reduce操作

22
{"name": "John", "age": 30, "city": "New York"}
考虑下面的 JSON 对象:{"name": "John", "age": 30, "city": "New York"}。
{
   "cells":[
      {
         "count":"1",
         "gdp_growth__avg":1.90575802503285,
         "geo__name":"united states of america",
         "time":1990
      },
      {
         "count":"1",
         "gdp_growth__avg":9.17893670154459,
         "geo__name":"china",
         "time":1991
      },
      {
         "count":"1",
         "gdp_growth__avg":-5.04693945214571,
         "geo__name":"russia",
         "time":1991
      },
      {
         "count":"1",
         "gdp_growth__avg":-0.0622142217811472,
         "geo__name":"botswana",
         "time":1991
      },
      {
         "count":"1",
         "gdp_growth__avg":14.2407063986337,
         "geo__name":"china",
         "time":1992
      },
      {
         "count":"1",
         "gdp_growth__avg":-14.5310737731921,
         "geo__name":"russia",
         "time":1992
      },
      {
         "count":"1",
         "gdp_growth__avg":3.55494453739944,
         "geo__name":"united states of america",
         "time":1992
      },
      {
         "count":"1",
         "gdp_growth__avg":13.9643147001603,
         "geo__name":"china",
         "time":1993
      },
      {
         "count":"1",
         "gdp_growth__avg":-8.66854034194856,
         "geo__name":"botswana",
         "time":1993
      },
      {
         "count":"1",
         "gdp_growth__avg":2.74204850437989,
         "geo__name":"united states of america",
         "time":1993
      },
      {
         "count":"1",
         "gdp_growth__avg":4.04272516401846,
         "geo__name":"united states of america",
         "time":1994
      },
      {
         "count":"1",
         "gdp_growth__avg":13.0806818010789,
         "geo__name":"china",
         "time":1994
      },
      {
         "count":"1",
         "gdp_growth__avg":-12.5697559787493,
         "geo__name":"russia",
         "time":1994
      },
      {
         "count":"1",
         "gdp_growth__avg":10.9249803004994,
         "geo__name":"china",
         "time":1995
      },
      {
         "count":"1",
         "gdp_growth__avg":-4.14352840666389,
         "geo__name":"russia",
         "time":1995
      },
      {
         "count":"1",
         "gdp_growth__avg":2.71655384149574,
         "geo__name":"united states of america",
         "time":1995
      },
      {
         "count":"1",
         "gdp_growth__avg":10.0085233990531,
         "geo__name":"china",
         "time":1996
      },
      {
         "count":"1",
         "gdp_growth__avg":3.79848988541973,
         "geo__name":"united states of america",
         "time":1996
      }
]
}

我想使用Map和Reduce生成一个新对象,其中包含上述JSON中所有国家的GDP增长总和,大致如下:

{  
  {
     "gdp_growth__avg":46.23,
     "geo__name":"united states of america",
  },
  {
     "gdp_growth__avg":16.23,
     "geo__name":"china",
  },
  {
     "gdp_growth__avg":36.23,
     "geo__name":"russia",
  },
  {
     "gdp_growth__avg":26.23, 
     "geo__name":"botswana",
     "time":1991
  }
 }

我已经查看了 mapreduce,但不确定如何最好地进行操作。

我认为这样做可能朝着正确的方向前进,但似乎并没有做到我期望的效果:

      var arr = [{x:1},{x:2},{x:4}];

      arr.reduce(function (a, b) {
        return {x: a.x + b.x};
      });

      console.log(arr); //Outputs that same initial array

虽然我知道这可能更好、更容易在服务器端完成,但我想知道是否可以在客户端使用JavaScript来实现我想要做的事情。有什么建议吗?提前感谢。


5
你希望使用 console.log(arr.reduce(…))reduce 方法不会改变数组(甚至不会重新赋值给变量),它只是返回一个值。 - Bergi
@Bergi,+1 因为我在这里学到了一些意外的东西。谢谢。 - MrEhawk82
如果您使用Node,mapreduce也可以在服务器端使用。 可能您的服务器可以运行它。 在js或python中最方便(是否有基于python的服务器?) - sqykly
5个回答

14
尝试这个:
var data = { cells:[...] };

var r = data.cells.reduce(function(pv, cv) {
    if ( pv[cv.geo__name] ) {
        pv[cv.geo__name] += cv.gdp_growth__avg;
    } else {
        pv[cv.geo__name] = cv.gdp_growth__avg;
    }
    return pv;
}, {});

console.log(r);

输出示例:

    { 
      'united states of america': 18.76051995774611,
      'china': 71.39814330096999,
      'russia': -36.291297610751,
      'botswana': -8.730754563729707 
   }

4
< p > Array.reduce 方法不会改变数组对象,它会把结果返回在一个新的数组中。


3
不,它不会返回新的数组。 - Bergi
1
根据我们使用的累加器类型而定,它可以是JSON对象甚至是JS Map,但array.map()会返回一个新数组。 - Shubham Dixit

1
根据@Hunan提供的解决方案,我添加了https://dev59.com/6H3aa4cB1Zd3GeqPkPRB#53294268以便获得没有重复项的结果。
我使用的最终解决方案是:
var data = {"cells": [...]};

data =  data.cells.map(function(datum) {
  return {
    geo__name: datum.geo__name,
    gdp_growth__avg: data.cells.filter(function(o) {
      return o.geo__name === datum.geo__name;
    }).reduce(function(sum, o) {
      return sum + o.gdp_growth__avg;
    }, 0)
  };
}).filter((obj, pos, arr) => {
        return arr.map(mapObj =>
              mapObj.geo__name).indexOf(obj.geo__name) == pos;
        });

JS Fiddle - https://jsfiddle.net/8b4z3yc5/


1
你可以尝试像这样做:

你可以尝试像这样做:

var data = {"cells": [...]};

data.cells.map(function(datum) {
  return {
    geo__name: datum.geo__name,
    gdp_growth__avg: data.cells.filter(function(o) {
      return o.geo__name === datum.geo__name;
    }).reduce(function(sum, o) {
      return sum + o.gdp_growth__avg;
    }, 0)
  };
})

毋庸置疑,您也可以从datum中提取其他属性,例如time。我没有这样做。

1

我试图用更好的时间复杂度来实现:

let ans = [];
let map = new Map();
ques.cells.forEach(x => {
if(map[x.geo__name]){
map.set(x.geo__name, map[x.geo__name] + x.gdp_growth__avg);
}else{
map.set(x.geo__name,x.gdp_growth__avg);
}
});

map.forEach((v,k)=>{
ans.push({"geo__name":k, "gdp_growth__avg":v});
});
ques.cells = ans;

解决方案看起来像这样:

[
  {
    "geo__name": "united states of america",
    "gdp_growth__avg": 3.79848988541973
  },
  {
    "geo__name": "china",
    "gdp_growth__avg": 10.0085233990531
  },
  {
    "geo__name": "russia",
    "gdp_growth__avg": -4.14352840666389
  },
  {
    "geo__name": "botswana",
    "gdp_growth__avg": -8.66854034194856
  }
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接