简体   繁体   English

如何正确计算数组中的出现次数?

[英]How to properly count occurrence in arrays?

I've got an in depth array (arrays of arrays). 我有一个深度数组(数组数组)。 I'm trying to make a decent function without using any extra methods and JS designs (like closures). 我正在尝试制作一个不错的功能而不使用任何额外的方法和JS设计(如闭包)。

This is a working solution (but a bad one because of the global variable) 这是一个有效的解决方案(但由于全局变量,这是一个糟糕的解决方案)

  var counter = 0;

  function findArray(array, item) {
      for (var i = 0; i < array.length; i++) {
          if (array[i] == item) {
              counter++;
          } else if (array[i].constructor === Array) {
              findArray(array[i], item);
          }
      }
      return counter;
  }

This is a none working solution with trying to avoid the global counter variable. 这是一个无法解决的问题,试图避免使用全局计数器变量。

  function findArray(array, item, counter) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] == item) {
            ++counter;
        }
        if (array[i].constructor === Array) {
            findArray(array[i], item, counter);
        }
    }
    return counter;
}

I would also like to avoid the counter input parameter since parameters should only be inputs and output variables shouldn't be inside the function params. 我还想避免计数器输入参数,因为参数应该只是输入,输出变量不应该在函数参数内。 Also I would like to avoid built in methods for instance slice etc. 另外我想避免内置的方法,例如切片等。

NOTE: The optimal performance of the algorithm should not be taken into consideration. 注意:不应考虑算法的最佳性能。

Is there a way to make this work with the counter being inside the function? 有没有办法使计数器在函数内部工作?

function call looks like this: 函数调用如下所示:

findArray([1,[1,2,3],1,[5,9,1],6],  1,0);

The output should be number 4 , in this example (integer 1 is the number to be searched in the array). 在此示例中,输出应为数字4 (整数1是要在数组中搜索的数字)。

This is a use case for recursion . 这是递归的用例。 (And also for Array.isArray , which was added in ES5.) findArray can call itself to find occurrences within arrays within the array: (也用于Array.isArray ,这是在添加ES5) findArray可以调用它本身到阵列内的数组内找到匹配项:

function findArray(array, item) {
    var counter = 0;
    for (var i = 0; i < array.length; i++) {
        var entry = array[i];
        if (entry == item){
            counter++;
        } else if (Array.isArray(entry)) {
            // Add in the count of occurrences in the array, and any arrays in it
            counter += findArray(entry, item);
        }
    }
    return counter;
}

It's also a use case for Array#reduce : 它也是Array#reduce一个用例:

function findArray(array, item) {
    return array.reduce(function(counter, entry) {
        if (entry == item) {
            ++counter;
        }
        if (Array.isArray(entry)) {
            counter += findArray(entry, item);
        }
        return counter;
    }, 0);
}

Array#reduce calls the callback once per entry in the array, passing in the accumulator on each call. Array#reduce在数组中的每个条目调用一次回调,在每次调用时传入累加器。 The initial value of the accumulator is provided as a second argument (optional in some cases, but not here); 累加器的初始值作为第二个参数提供(在某些情况下是可选的,但不是这里); the callback returns an updated version of the accumulator, which is the ultimate return value of reduce . 回调返回累加器的更新版本,这是reduce的最终返回值。

You have the right idea. 你有正确的想法。 You can modify the first recursive function like this: 您可以像这样修改第一个递归函数:

 function findArray(array, item) { var occurrences = 0; for (var i = 0; i < array.length; i++) { if (array[i] == item) { // Found an occurrence. occurrences++; } else if (array[i].constructor === Array) { // Add all occurrences in the sub-array i occurrences += findArray(array[i], item); } } return occurrences; } console.log(findArray([1, [1, 2, 3], 1, [5, 9, 1], 6], 1, 0)); 

Or shorten it using a reduce call, which has the same idea as above: 或者使用reduce调用缩短它,它具有与上面相同的想法:

 function findArray(array, item) { return array.reduce((occur, e) => occur + ((Array.isArray(e)) ? findArray(e, item) : (e == item)), 0); } console.log(findArray([1, [1, 2, 3], 1, [5, 9, 1], 6], 1, 0)); 

I believe this is the best solution yet: 我相信这是最好的解决方案:

const countOccurs = (arr, val) =>
  arr.reduce((prev, curr) => {
    if (Array.isArray(curr)) {
       return prev + countOccurs(curr, val);
    }

    if (curr !== val) {
      return prev;
    }

    return prev + curr;
  }, 0);

console.log(countOccurs([1,[1,2,3],1,[5,9,1],6], 1)); // 4

What we are doing here is, we are going throughout provided array with Array.prototype.reduce and checking for any occurances of the value. 我们在这里做的是,我们将使用Array.prototype.reduce提供数组,并检查值的任何出现。 If the actual value of the array (curr) is an array, the function is calling itself in order to check it and add the result to the counter (prev). 如果数组(curr)的实际值是一个数组,则该函数调用自身以检查它并将结果添加到计数器(prev)。

Working JSBin: http://jsbin.com/sayimecezo/edit?html,js,console,output 使用JSBin: http ://jsbin.com/sayimecezo/edit?html,js,console,output

You could simply flatten the nested arrays and just filter on it. 您可以简单地展平嵌套数组并对其进行过滤。 (Overriding prototype here just for convenience). (为方便起见,在这里覆盖原型)。

Array.prototype.countDeep = function (n) {
  return [].concat.apply([], this).filter(function (f) {
    return f === n;
  }).length;
}

console.log(([1,[1,2,3],1,[5,9,1],6]).countDeep(1));
//prints 4

Seeding to a memoised value is trivial (although unnecessary); 播种到备忘价值是微不足道的(虽然没必要);

Array.prototype.countDeep = function (n, seed) {
  return (seed || 0) + [].concat.apply([], this).filter(function (f) {
    return f === n;
  }).length;
}

console.log(([1,[1,2,3],1,[5,9,1],6]).countDeep(1, 200));
//prints 204

Another, maybe more comprehensible solution: 另一个可能更容易理解的解决方案:

function sumValues(array) {
    let counter = 0;
    for (let item of array) {
      if (Array.isArray(item)) {
        counter += sumValues(item);
      }
      else {
        counter += item;
      }
    }
    return counter;
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM