我正在寻找一种优雅的方法来确定JavaScript数组中出现率最高的元素(mode)。
例如,在以下数组中:
['pear', 'apple', 'orange', 'apple']
'apple'
元素是最常见的一个。
我正在寻找一种优雅的方法来确定JavaScript数组中出现率最高的元素(mode)。
例如,在以下数组中:
['pear', 'apple', 'orange', 'apple']
'apple'
元素是最常见的一个。
这只是一种模式,这里是一个快速但未经过优化的解决方案。它应该是O(n)。
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
f(modeMap [el] == null)
替换为if(!modeMap [el])
,因为在传递[2,3,3]时,modeMap [el]
是未定义而不是null,导致出现奇怪的数字问题。 - NaznodeMap
是一个 JavaScript 对象,可以实现为 B 树。当它被实现为"O(1)" 哈希表时,引擎并不知道 nodeMap
的大小,所以必须重新分配内存。每次重新分配内存都需要花费 log N
的时间,因此最终 O(n log n) 仍然是一个准确的描述。无论哪种方式,log N
因素太小,在大多数情况下并不重要。 - noɥʇʎԀʎzɐɹƆ自2009年以来,JavaScript已经有一些发展 - 我想再添加另一个选项。 我不太关心效率,直到实际出现问题,因此我对"优雅"代码的定义(如OP所规定)更偏向于可读性 - 当然这是主观的...
function mode(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
mode(['pear', 'apple', 'orange', 'apple']); // apple
在这个特定的例子中,如果集合中有两个或多个元素出现次数相同,则返回数组中最后出现的那个。值得注意的是,它会修改您的原始数组 - 如果您事先使用 Array.slice
调用可以避免这种情况。
return [...arr].sort()
。 - Daniel Pérez Rada.filter
函数,导致时间复杂度达到O(n * n * log(n)),而本应该是O(n)的算法。我认为“优雅”的解决方案应该是简洁、可维护、易读和高效的。 - ggorlenmode(['pear', 'apple', 'orange', 'apple', 'pear']); // 梨
- Flavio根据George Jempty的要求,让算法考虑并列情况,我提出了Matthew Flaschen算法的修改版。
function modeString(array) {
if (array.length == 0) return null;
var modeMap = {},
maxEl = array[0],
maxCount = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
maxEl += "&" + el;
maxCount = modeMap[el];
}
}
return maxEl;
}
现在将返回一个由&
符号分隔的众数元素字符串。当接收到结果时,可以在该&
元素上进行拆分,从而得到您的模式。
另一个选项是返回一个模式元素数组,如下所示:
function modeArray(array) {
if (array.length == 0) return null;
var modeMap = {},
maxCount = 1,
modes = [];
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
return modes;
}
在上面的示例中,您现在可以将函数的结果处理为模式数组。modes
设置为[array[0]]
作为初始值。这会确保你在modes
中有重复项。
这应该可以解决问题:var modes = []
。 - vdclouis==
实例更改为 ===
,以强制执行严格相等。 - Len Joseph基于 Emissary 的 ES6+ 回答,你可以使用 Array.prototype.reduce
来进行比较(而不是排序、弹出和可能会改变数组的元素),我认为这看起来非常简洁。
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
我默认为null,如果你正在过滤null作为可能的选项,这不会始终给出真实的响应,也许这可以成为一个可选的第二个参数。
与其他各种解决方案一样,缺点是它无法处理“绘制状态”,但是稍微复杂一些的reduce函数仍然可以实现这一点。
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
if(b[k]) b[k]++; else b[k]=1;
if(maxi < b[k]) { max=k; maxi=b[k] }
}
我将这个函数用作面试官的测试题,以下是我的解决方案:
const highest = arr => (arr || []).reduce( ( acc, el ) => {
acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1
acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el
return acc
}, { k:{} }).max
const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2]
console.log(highest(test))
const arr = ['hello', 'world', 'hello', 'again'];
const tally = (acc, x) => {
if (! acc[x]) {
acc[x] = 1;
return acc;
}
acc[x] += 1;
return acc;
};
const totals = arr.reduce(tally, {});
const keys = Object.keys(totals);
const values = keys.map(x => totals[x]);
const results = keys.filter(x => totals[x] === Math.max(...values));
这个解决方案的复杂度为 O(n)
:
function findhighestOccurenceAndNum(a) {
let obj = {};
let maxNum, maxVal;
for (let v of a) {
obj[v] = ++obj[v] || 1;
if (maxVal === undefined || obj[v] > maxVal) {
maxNum = v;
maxVal = obj[v];
}
}
console.log(maxNum + ' has max value = ' + maxVal);
}
findhighestOccurenceAndNum(['pear', 'apple', 'orange', 'apple']);
'use strict';
const histogram = iterable => {
const result = new Map();
for (const x of iterable) {
result.set(x, (result.get(x) || 0) + 1);
}
return result;
};
const mostCommon = iterable => {
let maxCount = 0;
let maxKey;
for (const [key, count] of histogram(iterable)) {
if (count > maxCount) {
maxCount = count;
maxKey = key;
}
}
return maxKey;
};
console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
Array.from()
将histogram(iterable)
进行包装:https://github.com/microsoft/TypeScript/issues/11209#issuecomment-303152976 - sMyles为了让代码易于阅读和维护,我分享以下内容:
function getMaxOcurrences(arr = []) {
let item = arr[0];
let ocurrencesMap = {};
for (let i in arr) {
const current = arr[i];
if (ocurrencesMap[current]) ocurrencesMap[current]++;
else ocurrencesMap[current] = 1;
if (ocurrencesMap[item] < ocurrencesMap[current]) item = current;
}
return {
item: item,
ocurrences: ocurrencesMap[item]
};
}