【问题标题】:Array of objects transformation to get count of each property in the array of object对象数组转换以获取对象数组中每个属性的计数
【发布时间】:2020-09-22 15:12:18
【问题描述】:

我有一个来自存储在角度范围变量中的数据库的对象数组。数组格式:

$scope.Divisions = [
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 2" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 3" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN3", "KPI": "Div1 KPI 5" },

    { "Division": "Div2", "Data Name": "DN3", "KPI": "Div2 KPI 6" },
    { "Division": "Div2", "Data Name": "DN4", "KPI": "Div2 KPI 6" },

    { "Division": "Div3", "Data Name": "DN3", "KPI": "Div3 KPI 7" },

    { "Division": "Div4", "Data Name": "DN3", "KPI": "Div4 KPI 7" },
]

我需要的转换后的数组是以以下格式获取每个部门的 distinct 数据名称和 distinct KPI 计数:

[

    ['Div1', 3, 5],
    ['Div2', 2, 1],
    ['Div3', 1, 1],
    ['Div4', 1, 1]
  ]

【问题讨论】:

  • 这对我来说似乎是一个循环——你试过吗?你有什么问题吗?
  • 你可能想要for..in,但可以使用任何循环。
  • 第二个条目是如何计算的? ['Div1', 3, 5]
  • 别介意,但这是纯粹的逻辑构建,你应该自己做。 ???如果这是由其他人完成的,那么它没有帮助,就像做你的工作一样。 ??????

标签: javascript arrays json angularjs


【解决方案1】:

使用reduce和map的一种方法示例(由于函数调用而比循环慢,但非常具有表现力和功能风格,对于小型数组,您永远不会注意到性能差异):

本质上:

  • 为要计数的键/属性创建常量const a = "Data Name"; const b = "KPI";
  • 生成一个空对象并通过循环输入数组中的项目来填充它(在此处使用 reduce 或在下面使用 for 循环)
  • 每次检查 Division 的属性是否已经存在于对象中(例如,检查对象是否首先具有 prop/key “Div1”等)
  • 如果不存在,则将此项目添加到具有该属性的对象中,并将 item[a] 和 item[b] 转换为单个项目数组。 (上面定义了 a 和 b)
  • 如果它已经存在,则推送到 item[a] 和 item[b](仅当值不在数组中时,通过检查 .indexOf() == -1 来判断)。
  • 将此对象转换为值数组
  • 在数组上映射(此处带有映射或下面的 for 循环)并转换为 [item.Division, item[a].length, item[b].length]

const Divisions = [
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 2" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 3" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN3", "KPI": "Div1 KPI 5" },
    { "Division": "Div2", "Data Name": "DN3", "KPI": "Div2 KPI 6" },
    { "Division": "Div2", "Data Name": "DN4", "KPI": "Div2 KPI 6" },
    { "Division": "Div3", "Data Name": "DN3", "KPI": "Div3 KPI 7" },
    { "Division": "Div4", "Data Name": "DN3", "KPI": "Div4 KPI 7" },
];

const a = "Data Name";
const b = "KPI";
const tempOutput = Divisions.reduce((aggObj, item) => {
  
  if (aggObj[item.Division]){
    if (aggObj[item.Division][a].indexOf(item[a]) == -1 ){
      aggObj[item.Division][a].push(item[a]);
    }
    if (aggObj[item.Division][b].indexOf(item[b]) == -1 ){
      aggObj[item.Division][b].push(item[b]);
    }
  }
  else {
    aggObj[item.Division] = item;
    aggObj[item.Division][a] = [item[a]];
    aggObj[item.Division][b] = [item[b]];
  }
  
  return aggObj;
}, {});

const output = Object.values(tempOutput).map(item => {  
  return [item.Division, item[a].length, item[b].length];
});

//console.log(tempOutput)
console.log(output)

输出:

[
  ["Div1", 3, 5],
  ["Div2", 2, 1],
  ["Div3", 1, 1],
  ["Div4", 1, 1]
]

使用 for 循环的另一种方法示例(更快,如果来自其他语言等,通常更容易):

const Divisions = [
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 2" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 3" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN3", "KPI": "Div1 KPI 5" },
    { "Division": "Div2", "Data Name": "DN3", "KPI": "Div2 KPI 6" },
    { "Division": "Div2", "Data Name": "DN4", "KPI": "Div2 KPI 6" },
    { "Division": "Div3", "Data Name": "DN3", "KPI": "Div3 KPI 7" },
    { "Division": "Div4", "Data Name": "DN3", "KPI": "Div4 KPI 7" },
];

const a = "Data Name";
const b = "KPI";
const aggObj = {};

for (let item of Divisions){
  
  if (aggObj[item.Division]){
    if (aggObj[item.Division][a].indexOf(item[a]) == -1 ){
      aggObj[item.Division][a].push(item[a]);
    }
    if (aggObj[item.Division][b].indexOf(item[b]) == -1 ){
      aggObj[item.Division][b].push(item[b]);
    }
  }
  else {
    aggObj[item.Division] = item;
    aggObj[item.Division][a] = [item[a]];
    aggObj[item.Division][b] = [item[b]];
  }

}

const tempOutput = Object.values(aggObj);
const output = [];
for (let item of tempOutput){  
  output.push([item.Division, item[a].length, item[b].length]);
}

//console.log(tempOutput)
console.log(output)

输出:

[
  ["Div1", 3, 5],
  ["Div2", 2, 1],
  ["Div3", 1, 1],
  ["Div4", 1, 1]
]

【讨论】:

  • 优秀的更新亚历克斯! - 只是关于性能的说明。 map/reduce 被设计为在您拥有解决大问题的机器集群时表现出色。这就是为什么它不能很好地解决这样的问题——cf en.wikipedia.org/wiki/MapReduce
  • @AlexL 他说的是真的。这个概念被大数据推广,尤其是。当谷歌发布了他们的数据处理优化时,他们使用 Map Reduce 来实现在本地移动大量数据以在传输之前进行转换,其中网络传输和 I/O 是主要瓶颈。它还简化了数据转换的表示和完成方式,因此它是一种高效的模式。他来自数据库管理员背景,所以这可能就是他的出身。
  • 好吧,我今天学到了很多 :) - 由于函数调用和对象初始化,map、reduce 等(高阶数组方法)比 for 循环慢。此外,map 和 reduce 的开发考虑了函数式风格和并行计算(我假设是因为我们想要纯函数,没有副作用等)? - 我不完全理解当聚合器是一个对象并且我们检查属性是否已经存在等时如何并行化reduce? (但也许这是你不做的一个例子,因为它不是纯粹的?) Map 很容易考虑被并行化。
  • 还有一些东西,比如原生数组优化,它们的性能可以提高一个数量级。依赖于 JS 引擎。
  • Gotchya,所以 V8 可能已经优化 .map() 比 for 循环更快,只要它是一个所有小整数的数组等? v8.dev/blog/elements-kinds(但 SpiderMonkey 可能没有优化它,所以它可能比那里的 for 循环慢等?)
【解决方案2】:

使用带有键的对象映射:[Division] 来跟踪 Divisions。使用 Set()s 跟踪重复项。映射到 .size() 以获取计数。
底部有详细注释代码。

Divisions = [
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 2" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 3" },
    { "Division": "Div1", "Data Name": "DN1", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 1" },
    { "Division": "Div1", "Data Name": "DN2", "KPI": "Div1 KPI 4" },
    { "Division": "Div1", "Data Name": "DN3", "KPI": "Div1 KPI 5" },

    { "Division": "Div2", "Data Name": "DN3", "KPI": "Div2 KPI 6" },
    { "Division": "Div2", "Data Name": "DN4", "KPI": "Div2 KPI 6" },

    { "Division": "Div3", "Data Name": "DN3", "KPI": "Div3 KPI 7" },

    { "Division": "Div4", "Data Name": "DN3", "KPI": "Div4 KPI 7" },
]

console.log(
 Object.values(
  Divisions.reduce((acc,{Division,['Data Name']:Name,KPI}) => {
    const [,Names,KPIs] = 
      acc[Division] = acc[Division] || [Division,new Set(),new Set()]
    Names.add(Name)
    KPIs.add(KPI)
    return acc
  },{})
 ).map(([div,names,kpis])=>([div,names.size,kpis.size]))
)
   // unwrap object map to values
   Object.values(
    // iterate over Divisions objects and create object map key: [Division] to track Divisions
    Divisions.reduce((acc,{Division,['Data Name']:Name,KPI}) => {
      // destructure Division 'Data Name' and KPI properties

      // destructure Names and KPIs Set()s
      const [,Names,KPIs] =
        acc[Division] = acc[Division] || [Division,new Set(),new Set()]
        // add to object map with key: [Division] and Set()s for Names and KPIs
      Names.add(Name)
      KPIs.add(KPI)
      return acc
    },{})
   // map Names Set() and KPIs Set() to .size count (number of occurrences)
   ).map(([div,names,kpis])=>([div,names.size,kpis.size]))

【讨论】:

  • 使用 Set 比检查 .indexOf() == -1 ? 是否被投票要干净得多。
猜你喜欢
  • 2022-12-08
  • 1970-01-01
  • 2012-11-17
  • 2022-11-27
  • 2019-02-10
  • 2022-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多