在许多应用场景中,我们需要找到数组中出现次数最多的元素。这里的关键点就是要如何统计每个元素出现的次数。在JS中,使用数组的reduce方法,无论是原始值还是对象值,都可以比较容易地实现。
下面,我们分别来看下如何在数组中查找最多次数的原始值和对象值。
1.找出数组中出现最多次数的原始类型元素
原始值很容易进行比较和计算频率。我们可以使用Array.prototype.reduce()来创建一个对象,这个对象的键为数组的唯一值,值为它们的频率。
注意,这里可以使用空值合并运算符(??)来初始化每个键的值为0(如果不存在,设为0),并在遇到相同值时将其加1。
最后,再使用Object.entries()和Array.prototype.reduce()来找到出现次数最多的值。
const mostFrequent = arr =>
Object.entries(
arr.reduce((a, v) => {
a[v] = (a[v] ?? 0) + 1;
return a;
}, {})
).reduce((a, v) => (v[1] >= a[1] ? v : a), [null, 0])[0];
mostFrequent(['a', 'b', 'a', 'c', 'a', 'a', 'b']); // 'a'
注:Object.entries():将对象转为键值的二维数组,类似 [[k1,v1],[k2, v2]]。
Array.prototype.reduce():简要来说,就是用于遍历数组,可以将上一次遍历的返回值用于本次遍历。第一个参数为上次遍历的返回值,第二个参数为本次遍历的值。
2.找出数组中出现最多次数的对象类型元素
在处理对象时,可以使用相同的方法,但需要一个映射函数来提取想要比较的值。由于原始值可以使用===运算符进行比较,可以提供一个默认的映射函数,该函数返回值本身。这样就可以使相同的实现同时适用于原始值和对象值。
const mostFrequent = (arr, mapFn = x => x) =>
Object.entries(
arr.reduce((a, v) => {
const k = mapFn(v);
a[k] = (a[k] ?? 0) + 1;
return a;
}, {})
).reduce((a, v) => (v[1] >= a[1] ? v : a), [null, 0])[0];
const people = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 28 },
{ name: 'John', age: 30 },
];
mostFrequent(people, p => p.age); // '30'
const nums = [1, 2, 3, 1, 2, 1];
mostFrequent(nums); // '1'
3.改进型
上面两种实现方式都有一个问题:返回值是字符串,不管数组本身是原始值还是其他类型。为了解决这个问题,可以使用Map来存储元素出现的次数,然后找到出现次数最多的值,因为Map中的键可以是任何类型。
这里需要改动的就是创建一个Map作为Array.prototype.reduce()的累加器,并使用Map.prototype.get()和Map.prototype.set()与之交互。最后,可以使用展开运算符(...)将Map转换为数组再找到出现最多的值。
const mostFrequent = (arr, mapFn = x => x) =>
[
...arr.reduce((a, v) => {
const k = mapFn(v);
a.set(k, (a.get(k) ?? 0) + 1);
return a;
}, new Map()),
].reduce((a, v) => (v[1] >= a[1] ? v : a), [null, 0])[0];
const people = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 28 },
{ name: 'John', age: 30 },
];
mostFrequent(people, p => p.age); // 30
const nums = [1, 2, 3, 1, 2, 1];
mostFrequent(nums); // 1
您在访问GitHub时是否经常无法访问?那么除了使用VPN以外,不妨到开源精选(Awesome Top)上逛一逛,这里精选了优秀的开源项目,在这里不仅可以看到项目Readme和官网地址,还可以了解一些开源的热门榜单和趋势!