对数组进行分组,过滤和计数

时间:2018-06-21 13:48:37

标签: javascript multidimensional-array group-by mapreduce

我正在使用js,并从响应中获得一个动态对象数组,如下所示:

[
  {fromCountry: "TE", toCountry: "FE", type: "new", status: "created"},
  {fromCountry: "TE", toCountry: "FE", type: "old", status: "cold"},
  {fromCountry: "CD", toCountry: "EG", type: "used", status: "hot"},
  {fromCountry: "CD", toCountry: "EG", type: "old", status: "hot"},
  {fromCountry: "CD", toCountry: "EG", type: "old", status: "cold"}
];

我想创建一个新数组,因此我想按fromCountry对它们进行分组,计算fromCountry并添加具有类型和值的嵌套数组。它应该看起来像是:

[
  { name: "TE", value: 2, child: [{ name: "new", value: 1 }, { name: "old", value: 1 }]}, 
  { name: "CD", value: 3, child: [{ name: "used", value: 1}, { name: "old", value: 2 }]}
]

(注意:我不想使用额外的JavaScript库)

能帮我吗?

4 个答案:

答案 0 :(得分:1)

您可以使用reduce将数组分组为一个对象。使用Object.values将对象转换为数组。

var arr = [{fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},{fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},{fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}];

var result = Object.values(arr.reduce((c, {fromCountry,type}) => {
  c[fromCountry] = c[fromCountry] || {name: fromCountry,value: 0,child: {}};
  c[fromCountry].child[type] = c[fromCountry].child[type] || {name: type,value: 0};
  c[fromCountry].child[type].value++;
  c[fromCountry].value++;
  return c;
}, {})).map(o => {
  o.child = Object.values(o.child);
  return o;
});

console.log(result);

答案 1 :(得分:1)

这是一种较慢的旧方法,它制作临时对象并根据所看到的内容递增值。

我敢肯定有一种更快的方法可以做到,所以请密切注意其他答案。

我已经注释了代码,但是随时可以问是否有什么不合理的地方。

var objs  = [{fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},{fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},{fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},{fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}];

// temp object and output array
let holding = {};
let output = [];

// for each object
for (let obj of objs) {
  // if it's the first time we've seen this fromCountry make it
  // and give it a value of 0
  if (!holding[obj.fromCountry]) holding[obj.fromCountry] = {
    value: 0
  };
  // if it's the first time we've seen this type make it and
  // give it a value of 0
  if (!holding[obj.fromCountry][obj.type]) holding[obj.fromCountry][obj.type] = {
    value: 0
  };
  
  // increment values
  holding[obj.fromCountry].value++;
  holding[obj.fromCountry][obj.type].value++
}

// Now we need to reformat the object

// for each key in the holding object
for (let key of Object.keys(holding)) {
  // make a new temp object in the right format
  let temp = {
    name: key,
    value: holding[key].value,
    child: []
  };
  // for each inner key in the holding object
  for (let innerKey of Object.keys(holding[key])) {
    // skip over value
    if (innerKey == "value") continue
    // make another temp object to be pushed to child
    let innertemp = {
      name: innerKey,
      value: holding[key][innerKey].value
    }
    // push inner temp object to child
    temp.child.push(innertemp);
  }
  //push whole temp object, now formatted correctly, to the output array
  output.push(temp);
}

console.log(output);

我希望这对您有帮助

答案 2 :(得分:1)

let arr = [
  {fromCountry:"TE",toCountry:"FE",type:"new",status:"created"},
  {fromCountry:"TE",toCountry:"FE",type:"old",status:"cold"},
  {fromCountry:"CD",toCountry:"EG",type:"used",status:"hot"},
  {fromCountry:"CD",toCountry:"EG",type:"old",status:"hot"},
  {fromCountry:"CD",toCountry:"EG",type:"old",status:"cold"}
];

let answer = [];

arr.forEach(x=> {
   if(!answer.some(y => y.name === x.fromCountry)){
     let newAnswer = {};
     newAnswer.name = x.fromCountry;
     newAnswer.value = 1;
     newAnswer.child = [];
    
     let child = {name: x.type, value: 1};
     newAnswer.child.push(child);
     answer.push(newAnswer);
  }else{
     let existAnswer = answer.find(y=>y.name === x.fromCountry);
     existAnswer.value++;
    
     if(existAnswer.child.some(z=>z.name === x.type)){
    	let childObj = existAnswer.child.find(z=>z.name === x.type);
        childObj.value++;
    }else{
       let newChildObj = {name: x.type, value: 1}
       existAnswer.child.push(newChildObj);
    }
  }
})

console.log(answer)

我们遍历整个数组,找到element的存在,如果发现则不加提示,如果找不到,则推送一个新对象。同样的逻辑也适用于子对象Obj 这可能不是一种优雅的方法,但我相信它更容易理解。您是非常主观的。

答案 3 :(得分:1)

您可以将函数用于任意数量的组。

此提议的特征是一个对象,该对象将单个对象保留为部分结果,分组结果和哈希(给定键的值),并将其作为组的访问器。

下划线属性_对于将播放负载对象与对象的哈希函数分开是必需的。

动态创建可能需要的子数组。

结果是一个嵌套结构,其深度与给定的groups数组相同。

function getGouped(array, groups) {
    var result = [],
        object = { _: { children: result } };

    array.forEach(function (a) {
        groups.reduce(function (r, k) {
            var name = a[k];
            if (!r[name]) {
                r[name] = { _: { name, count: 0 } };
                r._.children = r._.children || [];
                r._.children.push(r[name]._);
            }
            r[name]._.count++;
            return r[name];
        }, object);
    });
    return result;
}

var data = [{ fromCountry: "TE", toCountry: "F​​E", type: "new", status: "created" }, { fromCountry: "TE", toCountry: "F​​E", type: "old", status: "cold" }, { fromCountry: "CD", toCountry: "E​​G", type: "used", status: "hot" }, { fromCountry: "CD", toCountry: "E​​G", type: "old", status: "hot" }, { fromCountry: "CD", toCountry: "EG", type: "old", status: "cold" }];

console.log(getGouped(data, ['fromCountry', 'type']));
console.log(getGouped(data, ['fromCountry', 'type', 'status']));
.as-console-wrapper { max-height: 100% !important; top: 0; }