使用 reduce 缩短数组

5
使用reduce方法查找数组中某个元素出现的次数。该数组可能包含递归的子数组。
var foo = [
  1,
  [2, 3, 4], 
  4, [5,6,7], 4
];

bar(foo, 4) would return 3.


2
在你的例子中,为什么bar(foo, "a")会返回3,尽管它的输入中只出现了两次"a"呢? - Codor
@NorCallKnockOut 感谢您的澄清。 - Codor
foo不是一个对象,它是一个数组。 - edc65
5个回答

4

使用Array.prototype.forEach()方法,您可以通过一次调用计算所有项目。

forEach()方法会为数组中的每个元素执行一次提供的函数。

And a check if the element is an array, then the function is called again with the array as parameter.

var foo = ["a", ["b", "c", "d"], "a"],
    object = {};

function count(a, o) {
    a.forEach(function (b) {
        if (Array.isArray(b)) {
            count(b, o);
        } else {
            o[b] = (o[b] || 0) + 1;
        }
    })
}

count(foo, object);
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');


OP正在寻求一个递归函数。 - frogatto
4
这里调用了 count 函数的内部 count;这不就是递归的定义吗? - Dave Newton
哦,抱歉,我刚才没看到。:) - frogatto
1
@NinaScholz 好多了 :) - Aᴍɪʀ

4
尝试使用 Array.prototype.reduce 来实现。

var foo = [1, [2, 3, 4], 4, [5, 6, 7], 4];

function f(arr, item) {
  return arr.reduce(function (s, i) {
    if (Array.isArray(i)) return s+f(i, item);
    return s+(i==item?1:0);
  }, 0);
}

console.log(f(foo, 4))

函数f是一个递归函数。我们循环遍历所有项并将它们缩减为一个数字。该函数也会在所有内部数组上被调用,对于非数组项,我们只需检查它们是否等于所需项。


第一个返回值的第二个参数是做什么用的?the, 0); - NorCalKnockOut
1
啊,太棒了。这真的很干净。谢谢您。 - NorCalKnockOut
首先,s 将被初始化为初始值(0),然后它将调用函数并将返回值再次存储到 s 中,并在下一个元素上调用该函数。这就是 reduce 的工作原理。 - Aᴍɪʀ
1
reduce 的第二个参数默认不是 0。如果没有传递,则累加器将使用数组的第一个元素进行初始化,并对所有其他元素调用该函数。例如:a=["One","Two","Tree"] 比较 a.reduce((t,v)=>t+v.length)a.reduce((t,v)=>t+v.length,0)(使用函数调用的紧凑形式)。 - edc65

2

这里还有一种功能型的解释方式,不需要外部状态,但效率会更低。

var foo = [
  "a",
  ["b", "c", "d"],
  ["a", "b"],
  "a"
];

function flatten( arr ){
  return arr.reduce(function( ret, curr ){
    return ret.concat( Array.isArray( curr ) ? flatten( curr ) : [ curr ] );
  }, []);
}

function filterBy( arr, val ){
  return arr.filter(function( item ){
    return item === val;
  })
}

console.log( flatten( foo ) );
console.log( filterBy( flatten( foo ), 'a') );
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>


2
所以,如果您想进行递归(并且它可以在任何浏览器中正常工作):
var foo = [
    "a",
    ["b", "c", "d"], 
    "a"
];

function bar(arr, item) {
    var count = 0;
    if (arr.length == 0) {
        return 0;
    }

    for (var i = 0, l = arr.length; i < l; i++) {
        var v = arr[i];
        if (typeof v === "string") {
            if (v === item) {
                count++;
            }
        } else {
            count = count + bar(v, item);
        }
    }

    return count;
}

console.log(bar(foo, "a"));

1

使用underscore,您可以使用以下代码计算每个元素出现的次数:

_.countBy(_.flatten(array), _.identity)

所以函数foo可以像这样实现:

function bar(foo, element){
    return _.countBy(_.flatten(foo), _.identity)[element];
}
var foo = ["a", ["b", "c", "d"], "a"]
console.log(bar(foo, "a"));

虽然这个解决方案不是递归的,但我认为值得一提。


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