有一亿个keys要统计,应该用哪种集合?

本文详细解读了集合统计的四种关键概念:聚合统计(交并差)、排序统计(有序操作)、二值状态统计(bitmap)和基数统计(HyperLogLog)。讲解了如何运用这些统计方法于信息技术实践,包括数据处理和算法优化。

常用集合统计模式

  • 聚合统计
  • 排序统计
  • 二值状态统计
  • 基数统计

聚合统计

  • 指统计多个集合元素的聚合结果
  • 统计多个集合的共有元素(交集统计)
  • 把两个集合相比,统计其中一个集合独有的元素(差集统计)
  • 统计多个集合的所有元素(并集统计)

排序统计

  • list或者sorted_set类型
  • 有序统计

二值状态统计

  • 指集合元素的取值就只有 0 和 1 两种
  • bitmap,用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型

基数统计

  • 统计就是指统计一个集合中不重复的元素个数
  • set集合类型 数据量过大占用空间过高
  • HyperLogLog 计算基数所需空间是固定的,每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数
  • HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%
<think> 我们有一个对象数组,每个对象有多个属性。我们需要统计不同元素(即不同分组,比如不同类别)下,某一列(某个属性)出现的不同值(去重)的数量。 例如,假设我们有如下数据: [ { category: 'A', value: 1 }, { category: 'A', value: 2 }, { category: 'A', value: 2 }, { category: 'B', value: 2 }, { category: 'B', value: 3 } ] 要求按category分组,统计每个category中value的不同值数量。结果应该是: { A: 2, B: 2 } 在Node.js中,我们可以使用数组的reduce方法来实现。 步骤: 1. 使用reduce方法遍历数组,初始化一个空对象作为累加器。 2. 对于每个元素,获取其分组键(例如category)和要统计的属性(例如value)。 3. 如果累加器中没有这个分组键,则初始化一个空集合(例如Set)用于存储该分组下该属性的不同值。 4. 将当前元素的属性值添加到对应的集合中(由于Set会自动去重,所以不用担心重复)。 5. 最后,将累加器对象转换为每个分组对应的集合大小(即不同值的数量)。 另一种方法:在reduce中,我们也可以不使用Set,而使用对象来模拟集合(但Set更直接)。 但是注意:我们最终要的是每个分组下不同值的数量,所以我们可以在每一步都记录每个分组下出现的不同值。 代码示例: </think> # Node.js 统计对象数组中不同元素下某一列的不同值数量 在 Node.js 中统计对象数组里不同元素下某一列出现的不同值数量,可以通过以下方法实现: ## 核心解决方案 ```javascript // 示例数据:员工在不同部门中的技能 const employees = [ { department: 'Engineering', skill: 'JavaScript' }, { department: 'Engineering', skill: 'Python' }, { department: 'Engineering', skill: 'JavaScript' }, // 重复值 { department: 'Design', skill: 'UI/UX' }, { department: 'Design', skill: 'Figma' }, { department: 'Marketing', skill: 'SEO' }, { department: 'Marketing', skill: 'SEO' }, // 重复值 { department: 'Marketing', skill: 'Analytics' } ]; // 统计各部门的不同技能数量 function countUniqueValuesByGroup(array, groupKey, valueKey) { const result = {}; array.forEach(item => { const group = item[groupKey]; const value = item[valueKey]; // 如果分组不存在,初始化分组 if (!result[group]) { result[group] = { values: new Set(), // 使用Set自动去重 count: 0 }; } // 添加值到Set,Set会自动处理重复值 const prevSize = result[group].values.size; result[group].values.add(value); // 如果添加后Set大小变化,则计数增加 if (result[group].values.size > prevSize) { result[group].count++; } }); // 转换结果为所需格式 const simplifiedResult = {}; Object.keys(result).forEach(group => { simplifiedResult[group] = result[group].count; }); return simplifiedResult; } // 使用示例:统计各部门的不同技能数量 const skillCounts = countUniqueValuesByGroup(employees, 'department', 'skill'); console.log(skillCounts); // 输出: { Engineering: 2, Design: 2, Marketing: 2 } ``` ## 优化内存使用的版本 ```javascript function optimizedCountUniqueValues(array, groupKey, valueKey) { return array.reduce((acc, item) => { const group = item[groupKey]; const value = item[valueKey]; if (!acc[group]) { acc[group] = new Set(); } acc[group].add(value); return acc; }, {}); } // 使用示例 const uniqueValues = optimizedCountUniqueValues(employees, 'department', 'skill'); console.log(uniqueValues); // 转换结果为计数 const counts = Object.fromEntries( Object.entries(uniqueValues).map(([key, set]) => [key, set.size]) ); console.log(counts); // 输出: { Engineering: 2, Design: 2, Marketing: 2 } ``` ## 处理大型数据集的流式处理方案 ```javascript const fs = require('fs'); const readline = require('readline'); async function streamCountUniqueValues(filePath, groupKey, valueKey) { const result = new Map(); const fileStream = fs.createReadStream(filePath); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity }); for await (const line of rl) { try { const item = JSON.parse(line); const group = item[groupKey]; const value = item[valueKey]; if (!result.has(group)) { result.set(group, new Set()); } result.get(group).add(value); } catch (error) { console.error('解析错误:', error.message); } } // 转换为对象格式 const formattedResult = {}; for (const [group, set] of result) { formattedResult[group] = set.size; } // 监控内存使用 const memoryUsage = process.memoryUsage(); console.log('内存使用情况:'); console.log(`堆总量: ${(memoryUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`); console.log(`已用堆: ${(memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`); return formattedResult; } // 使用示例 (假设有employees.json文件) // streamCountUniqueValues('employees.json', 'department', 'skill') // .then(console.log); ``` ## 数学表达与原理 对于包含$n$个元素的对象数组,统计不同元素$g$下属性$v$的不同值数量,可以用公式表示为: $$count(g) = |\{v_i | \exists i \in [1,n] \text{ where } g_i = g\}|$$ 其中: - $g$表示分组元素 - $v$表示要统计的属性值 - $|\cdot|$表示集合的基数(元素个数) - $i$表示数组索引 使用Set数据结构的时间复杂度为$O(1)$(平均情况),空间复杂度为$O(k)$,其中$k$是不同值的数量[^1]。 ## 性能优化技巧 1. **使用Set而不是数组**:Set自动处理重复值,查找效率为$O(1)$ 2. **流式处理大型数据**:避免一次性加载整个数组到内存 3. **内存监控**:使用`process.memoryUsage()`监控内存 ```javascript console.log(process.memoryUsage()); ``` 4. **批处理**:对超大型数据可分批次处理: ```javascript const batchSize = 1000; for (let i = 0; i < array.length; i += batchSize) { const batch = array.slice(i, i + batchSize); // 处理批次数据 } ``` ## 实际应用场景 1. 电商平台:统计每个商品类别的不同卖家数量 2. 日志分析:统计不同API端点的不同用户ID数量 3. 社交网络:统计不同城市的用户兴趣爱好种类
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值