JavaScript - 找出数组中出现最频繁的数字,即使有两个

4

如果我有一个数字数组,想找到出现次数最多的数字,但是有两个或以上数字出现频率相同,我该怎么办?例如下面的例子应该返回1和7,但我只得到了7。希望得到帮助。

let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];

function findMode(numbers) {
    let counted = numbers.reduce((acc, curr) => { 
        if (curr in acc) {
            acc[curr]++;
        } else {
            acc[curr] = 1;
        }

        return acc;
    }, {});

    let mode = Object.keys(counted).reduce((a, b) => counted[a] > counted[b] ? a : b);

    return mode;
}

console.log(findMode(arr));

5个回答

3
你可以把相同的项分组为子数组,然后按照子数组长度排序并检索具有相同数组长度的第一个值,就像这样:

const arr = [1, 1, 2, 3, 4, 5, 6, 7, 7],
  output = arr
    .sort((a, b) => a - b)
    .reduce(
      (acc, cur, i, { [i - 1]: last }) =>
        (cur === last ? acc[acc.length - 1].push(cur) : acc.push([cur])) && acc,
      []
    )
    .sort((a, b) => b.length - a.length)
    .reduce(
      (a, b, _, [first]) => (first.length === b.length ? [...a, b[0]] : a),
      []
    );

console.log(output);


这仅适用于已排序的输入数组,即当重复元素紧挨在一起时。例如,9 将不会从 [1, 1, 9, 2, 3, 4, 5, 9, 6, 7, 7] 中被选中。 - Carsten Massmann
1
的确,我在开头添加了一个排序。 - Guerric P

2

您可以将数组用作累加器。

let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];

function findMode(numbers) {
    let counted = numbers.reduce((acc, curr) => { 
        if (curr in acc) {
            acc[curr]++;
        } else {
            acc[curr] = 1;
        }

        return acc;
    }, {});

    let mode = Object.keys(counted).reduce((acc, curr) => {
      if(!acc.length || counted[curr] > counted[acc[0]]) return [curr];
      if(counted[curr] === counted[acc[0]]) acc.push(curr);
      return acc;
    }, []);

    return mode;
}
console.log(findMode(arr));

或者,您可以找到最高频率,然后使用 filter 查找具有该频率的数字。

let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];

function findMode(numbers) {
    let counted = numbers.reduce((acc, curr) => { 
        if (curr in acc) {
            acc[curr]++;
        } else {
            acc[curr] = 1;
        }
        return acc;
    }, {});
    let mode = Math.max(...Object.values(counted));
    return Object.keys(counted).filter(x => counted[x] === mode);
}
console.log(findMode(arr));


通过将数字作为对象属性使用,您将它们转换为字符串。 - Guerric P
@GuerricP 原始解决方案也返回一个字符串,因此这似乎不是一个问题。 - Unmitigated

2
你可以在第一个循环中跟踪最大出现次数max,然后使用Array#filter获取具有该值的keys

function findMode(numbers) {
    let max = 0;
    const counted = numbers.reduce((acc, curr) => { 
        if (curr in acc) acc[curr]++;
        else acc[curr] = 1;
        if(acc[curr] > max) max = acc[curr];
        return acc;
    }, {});
    const mode = Object.keys(counted)
      .filter(key => counted[key] === max)
      .map(Number);
    return mode;
}

console.log( findMode([1, 1, 2, 3, 4, 5, 6, 7, 7]) );


通过将数字作为对象属性使用,您将它们转换为字符串。 - Guerric P

0

由于整个操作都发生在函数范围内,我们也可以使用两个.forEach()循环来完成:在第一个循环中,我们收集计数,在第二个循环中,我们使用“获胜者”组装结果数组。

通过使用map来收集计数,我们避免了使用普通对象时会发生的类型转换为字符串的情况。

let arr = [1, 1, 2, "1", 3, 4, "1", 5, 6, 7, 7];

function findMode(nums) {
  let cn=new Map(),mx=0,res;
  nums.forEach(n=>cn.set(n,(cn.get(n)||0)+1));
  [...cn.entries()].forEach(([v,c])=>{
   if(c>mx) {res=[v];mx=c}
   else if (c===mx) res.push(v) });
  return res;
}
console.log(findMode(arr));


0
你可以在单个循环中直接获取计数、最大频率和值来减少数组。

function findMode(numbers) {
    return numbers
        .reduce((r, v) => { 
            r[v] = (r[v] || 0) + 1;
            if (r[v] === r.max) r.values.push(v);
            if (r[v] > r.max) {
                r.max = r[v];
                r.values = [v];
            }
            return r;
        }, { max: 0, values: [] })
        .values;
}

console.log(findMode([1, 1, 2, 3, 4, 5, 6, 7, 7]));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接