JavaScript数组中对象的分组依据,计数不同,总计

时间:2018-08-22 16:31:38

标签: javascript

这可能是补救措施,但我不知道。我试过使用d3并与lodash一起玩以获得有效的解决方案,但没有得到任何结果。

我在JavaScript中有一个对象数组。如果[Selected]值为true,我想创建一个按[Version Name]分组的对象,该对象的计数为 distinct 个区域,每个版本的总数之和。例如对象...

[ 
     { Selcted: false, Version Name: "aaa", Zone: "11111", Value: 5 },
     { Selcted: false, Version Name: "aaa", Zone: "11111", Value: 10 },
     { Selcted: true, Version Name: "aaa", Zone: "11111", Value: 15 },
     { Selcted: true, Version Name: "aaa", Zone: "11111", Value: 20 },
     { Selcted: true, Version Name: "aaa", Zone: "22222", Value: 25 },
     { Selcted: true, Version Name: "bbb", Zone: "22222", Value: 30 },
     { Selcted: true, Version Name: "bbb", Zone: "22222", Value: 35 },
     { Selcted: true, Version Name: "bbb", Zone: "2222", Value: 40 }
]

应返回

的结果
[ 
     { Version Name: "aaa", Zone Count: "2", Value Sum: 50 },
     { Version Name: "bbb", Zone Count: "1", Value Sum: 105 },
]

9 个答案:

答案 0 :(得分:1)

这使用了我最喜欢的groupBy函数:)一旦获得了分组,您就可以进行另一个分组以获取区域计数,并进行归约以获取总和。

简而言之

const byName = groupBy(input.filter(it => it.Selcted), it => it['Version Name'])

const output = Object.keys(byName).map(name => {
  const byZone = groupBy(byName[name], it => it.Zone)
  const sum = byName[name].reduce((acc, it) => acc + it.Value, 0)
  return {
    'Version Name': name,
    ZoneCount: Object.keys(byZone).length,
    ValueSum: sum
  }
})

别忘了,您需要在“版本名称”两边加上引号,才能将其用作键。

这是您的数据集的有效示例。

function groupBy(a, keyFunction) {
  const groups = {};
  a.forEach(function(el) {
    const key = keyFunction(el);
    if (key in groups === false) {
      groups[key] = [];
    }
    groups[key].push(el);
  });
  return groups;
}

const input = [{
    Selcted: false,
    'Version Name': "aaa",
    Zone: "11111",
    Value: 5
  },
  {
    Selcted: false,
    'Version Name': "aaa",
    Zone: "11111",
    Value: 10
  },
  {
    Selcted: true,
    'Version Name': "aaa",
    Zone: "11111",
    Value: 15
  },
  {
    Selcted: true,
    'Version Name': "aaa",
    Zone: "11111",
    Value: 20
  },
  {
    Selcted: true,
    'Version Name': "aaa",
    Zone: "22222",
    Value: 25
  },
  {
    Selcted: true,
    'Version Name': "bbb",
    Zone: "22222",
    Value: 30
  },
  {
    Selcted: true,
    'Version Name': "bbb",
    Zone: "22222",
    Value: 35
  },
  {
    Selcted: true,
    'Version Name': "bbb",
    Zone: "2222",
    Value: 40
  }
]

const byName = groupBy(input.filter(it => it.Selcted), it => it['Version Name'])

const output = Object.keys(byName).map(name => {
  const byZone = groupBy(byName[name], it => it.Zone)
  const sum = byName[name].reduce((acc, it) => acc + it.Value, 0)
  return {
    'Version Name': name,
    ZoneCount: Object.keys(byZone).length,
    ValueSum: sum
  }
})


console.log(output)

答案 1 :(得分:1)

使用filter来获取所需的物品后,您可以分两步进行操作,先进行reduce,然后再进行map

let input = [ 
     { Selcted: false, "Version Name": "aaa", Zone: "11111", Value: 5 },
     { Selcted: false, "Version Name": "aaa", Zone: "11111", Value: 10 },
     { Selcted: true, "Version Name": "aaa", Zone: "11111", Value: 15 },
     { Selcted: true, "Version Name": "aaa", Zone: "11111", Value: 20 },
     { Selcted: true, "Version Name": "aaa", Zone: "22222", Value: 25 },
     { Selcted: true, "Version Name": "bbb", Zone: "22222", Value: 30 },
     { Selcted: true, "Version Name": "bbb", Zone: "22222", Value: 35 },
     { Selcted: true, "Version Name": "bbb", Zone: "2222", Value: 40 }
]

var result = input.filter(x => x.Selcted)
                  .reduce( (acc, curr) => {
                      let item = acc.find(x => x.version == curr["Version Name"]);
                      if(!item){
                          item = {version: curr["Version Name"], zones:{}}
                          acc.push(item);
                      }
                      item.zones[curr.Zone] = (item.zones[curr.Zone] || 0) + curr.Value
                      return acc;
                  },[])
                  .map(x => ({
                    "Version Name": x.version,
                    "Zone Count": Object.keys(x.zones).length,
                    "Value Sum": Object.values(x.zones).reduce( (a,b) => a+b ,0)
                  }))
                  
console.log(result);

答案 2 :(得分:1)

您可以使用lodash并通过使用分组获得所需的计数

  • _“ Seq”方法,用于链接lodash方法,
  • _.groupBy用于按"Version Name"分组
  • _.map用于结果集
  • _.uniqBy用于计数不同的值,
  • _.sumBy用于求和Value
  • _.value用于获取带有对象作为结果集的数组。

var data = [{ Selected: false, "Version Name": "aaa", Zone: "11111", Value: 5 }, { Selected: false, "Version Name": "aaa", Zone: "11111", Value: 10 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 15 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 20 }, { Selected: true, "Version Name": "aaa", Zone: "22222", Value: 25 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 30 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 35 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 40 }],
    result = _(data)
        .groupBy('Version Name')
        .map((array, key) => ({
            "Version Name": key,
            "Zone Count": _.uniqBy(array, 'Zone').length,
            "Value Sum": _.sumBy(array, 'Value')
        }))
        .value();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

答案 3 :(得分:0)

假设您的数据数组名为arr

const summed = Object.values(
  arr
    .filter(x => x.Selected)
    .reduce((acc, obj) => {
      if (!acc[obj["Version Name"]) {
        acc["Version Name"] = {
          "Version Name": acc["Version Name"],
          "Zones": new Set(),
          "Value Sum": 0,
        };
      }
      acc["Version Name"]["Value Sum"] += obj.Value;
      acc["Version Name"].Zones.add(obj.Zone);
      return acc;
    })
).map(obj => {
  return {
    "Version Name": obj["Version Name"],
    "Zone Count": obj.Zones.size,
    "Value Sum": obj["Value Sum"],
  };
});

首先,我们通过对版本名称进行哈希处理,筛选出未选中的对象,并通过版本名称获得不同的对象数组,对值进行聚合,使用Set跟踪不同的区域,然后使用Object.values将哈希返回排列成一个数组。然后我们在其上进行映射,以获取每个Set的区域计数。

答案 4 :(得分:0)

您可以为此使用Array.reduce()Set()来创建地图,然后在地图上Object.values()会给您想要的结果。尝试以下操作:

let arr = [ { Selected: false, VersionName: "aaa", Zone: "11111", Value: 5 }, { Selected: false, VersionName: "aaa", Zone: "11111", Value: 10 }, { Selected: true, VersionName: "aaa", Zone: "11111", Value: 15 }, { Selected: true, VersionName: "aaa", Zone: "11111", Value: 20 }, { Selected: true, VersionName: "aaa", Zone: "22222", Value: 25 }, { Selected: true, VersionName: "bbb", Zone: "22222", Value: 30 }, { Selected: true, VersionName: "bbb", Zone: "22222", Value: 35 }, { Selected: true, VersionName: "bbb", Zone: "2222", Value: 40 } ];
let set = new Set();

let result = Object.values(arr.reduce((a, curr)=>{
  if(curr.Selected){
    a[curr.VersionName] = a[curr.VersionName] || {versionName : curr.VersionName, ZoneCount : 0, ValueSum : 0} ;
      if(!set.has(curr.Zone+"_"+curr.VersionName)){
        a[curr.VersionName].ZoneCount += 1;
        set.add(curr.Zone+"_"+curr.VersionName);
      }
     a[curr.VersionName].ValueSum += curr.Value;
   }
  return a;
},{}));

console.log(result);

答案 5 :(得分:0)

我认为最简单的解决方案是将reducefilter一起使用

const selectedObjects = objects.filter((i) => i.Selected === true);

const results = selectedObjects.reduce((items, obj) => {
    let item = items.find((i) => i.VersionName === obj["Version Name"]);
    if (!item) {
        const ZoneCount = selectedObjects
            .filter((i) => i["Version Name"] === obj["Version Name"])
            .reduce((zones, obj) => {
                if (zones.indexOf(obj.Zone) === -1) {
                    zones.push(obj.Zone);
                }
                return zones;
            }, []).length;

        item = {
            ValueSum: 0,
            VersionName: obj["Version Name"],
            ZoneCount
        };

        items.push(item);
    }

    item.ValueSum += obj.Value;

    return items;
}, []);

console.log(results);

答案 6 :(得分:0)

首先过滤掉,然后分组,然后计算区域,因为我们需要在数组上使用heler函数:

  Array.prototype.groupBy = function groupBy(key) {
    const hash = {}, result = [];
    for(const el of this) {
       if(hash[ el[key] ]) {
         hash[ el[key] ].push(el);
       } else {
         result.push({
           key: el[key],
           values: hash[ el[key] ] = [ el ],
        });
     }
  }
  return result;
 };

 Array.prototype.key = function(key) {
   return this.map(el => el[key]);
 };

 Array.prototype.sum = function(key) {
  return this.reduce((total, el) => total + (key ? el[key] : el), 0);
 };

 Array.prototype.unique = function() {
   return [...new Set(this)];
 };

这实际上是很多代码,但是现在我们可以使用它们来构建结果:

  const result = array
       .filter(el =>  el.Selected)
       .groupBy("Version Name")
       .map(({ key, values }) => ({
         "Version Name": key,
         "Value Sum": values.sum("Sum"),
         "Zone Count":values.key("Zone").unique().length,
       }));

答案 7 :(得分:0)

我觉得您可以使用一次reduce,使用2个hash(因为您希望在vanilla JS中使用它)来完成此操作,并且只遍历整个数组一次。似乎您想在一开始进行过滤,但是您可以执行一系列操作,例如

filter(...).map(...).reduce().continue().blabla(...)(使用纯js或lodashunderscore ...等任何库)

现在

要么分离出逻辑,然后执行更易于理解和维护的更整洁的操作(例如:Nina Scholz's answer中有一个)

或者,仅使用reduce(或简单的for循环)遍历它一次并执行所有必要的操作,并尝试避免一系列遍历,而是像散列(或任何合适的方法)那样使用更多的内存来对其进行缓存。

这是我为此创建的示例

let input = [{ Selected: false, "Version Name": "aaa", Zone: "11111", Value: 5 }, { Selected: false, "Version Name": "aaa", Zone: "11111", Value: 10 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 15 }, { Selected: true, "Version Name": "aaa", Zone: "11111", Value: 20 }, { Selected: true, "Version Name": "aaa", Zone: "22222", Value: 25 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 30 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 35 }, { Selected: true, "Version Name": "bbb", Zone: "22222", Value: 40 }],
    vn = "Version Name", vs = "Value Sum", zc = "Zone Count", //Target Keys
    {res} = input.reduce(({res={}, hash={}}, e) => {
	if(e.Selected) {
		let vrsn = e[vn],
		vrsnGrp = (hash[vrsn] = hash[vrsn] || {});
		res[vrsn] = (res[vrsn] || {[vn]: vrsn, [vs]:0, [zc]: 0});
		res[vrsn][vs] += e.Value;
		res[vrsn][zc] += !vrsnGrp[e.Zone];
		vrsnGrp[e.Zone] = true;
	}
	return {res, hash};
}, {});

console.log(Object.values(res));
.as-console-wrapper { max-height: 100% !important; top: 0; }

它将执行所有必要的操作(过滤,分组等),但仅遍历输入数组一次,它将仅使用一个额外的哈希(因为res也是此处的哈希)。 / p>

答案 8 :(得分:0)

let campos = ["classificacao", "count_tables"];
let conteudo = mysql_information_schema[cliente][projeto][dbName].conteudo;
let count = {};
conteudo.forEach( (tabela)=> { //agrupa dentro de count
     count[tabela.classificacao] = count[tabela.classificacao] > 0 ? count[tabela.classificacao] + 1 : 1 ;
});
//converte o objeto para o padrão do array object da view
conteudo =  Object.keys(count).map( (key)=>{return {classificacao: key,  count_tables: count[key]} } );