5

I apologize if this has been asked before but I could not find an answer. How do I loop through an array with nested arrays and in the console print out the number of instances an item appears?

So console.log should print out the number 2 for the name "bob" because "bob" appears twice in the array.

Here is my array and what I have so far:

    var names = ["bob", ["steve", "michael", "bob", "chris"]];

    function loop(arr, item) {
      for (var i = 0; i < arr.length; i++) {
        if (arr[i] instanceof Array) {
          loop(arr[i], item);
        } else {
          if (arr[i] == item) {
            console.log(arr[i]);
          }
        }
      }
    }

    loop(names, "bob");

3
  • 2
    Your recursion looks about fine. Now you just have to count instances (and return that value), instead of console.logging them. Try it! Commented Mar 8, 2016 at 18:03
  • Possible duplicate of How to flatten nested array in javascript? Commented Mar 8, 2016 at 18:11
  • Also answered here: stackoverflow.com/a/15030117/4494577 Commented Mar 8, 2016 at 18:15

6 Answers 6

8

here you go, note that you can keep the counter value internally, to keep the rest of your code cleaner:

var names = ["bob", ["steve", "michael", "bob", "chris"]];

function loop(arr, item) {
  var result = 0;
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] instanceof Array) {
      result += loop(arr[i], item);
    } else {
      if (arr[i] == item) {
        result++;
      }
    }
  }
  return result;    
}


var result = loop(names, "bob");
console.log(result);
Sign up to request clarification or add additional context in comments.

Comments

3

You need a counter

function loop(arr, item) {
    var count = 0;
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] instanceof Array) {
            count += loop(arr[i], item);
        } else {
            if (arr[i] == item) {
                count++;
                console.log(arr[i]);
            }
        }
    }
    return count;
}

var names = ["bob", ["steve", "michael", "bob", "chris"]],
    count = loop(names, "bob");

document.write(count);

Comments

3

You could also use reduce

var names = ['bob', ['steve', 'michael', 'bob', 'chris', ['bob']]];

function count(item, items) {
  return items.reduce(function(sum, x) {
    if (Array.isArray(x)) return sum + count(item, x);
    if (x === item) return sum + 1;
    return sum;
  }, 0);
}

count('bob', names); // => 3

Another option would be to use more generic functions and chain them together.

  1. flatten the input array; [1,[2,3,4,5,[6]]] => [1,2,3,4,5,6]
  2. filter the flattened array where each element matches your search element
  3. return the length of the filtered array

That would look something like this

flatten(names).filter(x => x === 'bob').length

I'll leave the implementation of flatten as an exercise for you

Comments

1

As of 2020, we have Array.prototype.flat method which makes this task trivial.

Here you go.

const names = ["bob", ["steve", "michael", "bob", "chris"]];
const nestedNames = ["bob", ["steve", ["michael", ["bob", ["chris"]]]]];

function countName(array, name, depth = Infinity) {
  return array.flat(depth).reduce((acc, cur) => acc + (cur === name ? 1 : 0), 0);
}

console.log(countName(names, 'bob')); // 2
console.log(countName(names, 'chris')); // 1

console.log(countName(nestedNames, 'bob')); // 2
console.log(countName(nestedNames, 'chris')); // 1

Comments

0

A pure form of recursion, No outside variable required.

Try to modify your logic like below,

var names = ["bob", ["steve", "michael", "bob", "chris",["bob"]]];
function loop(arr, item, cnt){
   cnt = cnt || 0;
   for(var i = 0; i < arr.length; i++){
     if(arr[i]==item){ cnt++; }
     else if(arr[i] instanceof Array) { return loop(arr[i], item, cnt); } 
   }
   return cnt;
}

loop(names,"bob"); //3

var names = ["bob", ["steve", "michael", "bob", "chris", ["bob"],["bob",["bob"]]], "bob"];

function find(arr, txt, cnt, match) {
  cnt = cnt || 0;
  match = match || 0;
  if (arr[cnt] === txt) { match++; } 
  else if (arr[cnt].push) { match = find(arr[cnt], txt, 0, match); } 
  if (++cnt === arr.length) { return match; }
  return find(arr, txt, cnt, match);
}

alert(find(names, "michael")); //6

4 Comments

The introduction of another argument is unnecessary.
It's an improvement, although I would take the other answers as they don't rely on propagating yet another object down recursion and instead propagate the result up.
@Aldehir That's their approach and this is mine. There is no necessity to stick with the larger audience. This is not going to make any impact in performance.
This won't work in the case that another matching item exists later on in the top level array, for example ["bob", ["steve", "michael", "bob", "chris",["bob"]], "bob"], because it returns too early. Changing return loop(arr[i], item, cnt); to cnt = loop(arr[i], item, cnt); will fix this.
0

const names = ["bob", ["steve", "michael", "bob", "chris"]];

function loop(arr, item) {
    let res = 0;

    for (let v of arr) {
        if (typeof v === 'object') res += loop(v, item);
        if (v === item) res++;
    }

    return res;    
}



const result = loop(names, "bob");
console.log(result);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.