How to loop through 2 arrays as arguments with for, for of, or forEach

Viewed 232

I am somewhat new to Javascript so I'll try my best to explain this properly. I'm using vanilla JS on the fizz buzz loop, but I'm trying to do it slightly differently, I think.

I've put both sets of numbers in 2 separate arrays, and successfully did it with a for loop by counting up to 20, but I didn't use the arrays to do it. However, I want to use the variables passed as arguments to check if the values are true so the appropriate response can be printed, either fizz, buzz, or fizzbuzz.

My code for the for loop is:

const fizz = [3, 6, 9, 12, 15, 18];
const buzz = [5, 10, 15, 20];

for (let i = 1; i <= 20; i++){
    if(i % 3 === 0 && i % 5 === 0) {
        console.log(i + " fizz buzz");
    } else if(i % 3 === 0){
       console.log(i + " fizz");
    } else if(i % 5 === 0) {
    console.log(i + " buzz");
    } else {
        console.log(i);
    }
}
.as-console-wrapper {max-height: 100%!important;top:0}

As I said, that runs successfully.

I did edit the for loop with the fizz & buzz variables, with no success. I tried using index & indexOf, but I'm not sure how to code that.

I also used for of & forEach, but I'm struggling on how to code those as well.

I know it's messy, but how I coded my forEach method looks like this:

fizz.forEach(function(value) {
     if (i === fizz.indexOf[value]) {
        console.log(value);
         i++;
     }
});

Ultimately my questions with all of this is - how do I pass those arrays as arguments to be looped through and checked if their values are true or not? And, which is the best way of doing it?

3 Answers

You can test if the number you're iterating is inside of any of the arrays with the function some. For example,

const fizz = [3, 6, 9, 12, 15, 18];
const buzz = [5, 10, 15, 20];

for(let i = 1; i <= 20; i++){
    const isFizz = fizz.some(f => f === i);
    const isBuzz = buzz.some(b => b == i);
    if(isFizz && isBuzz){
        console.log(i + " fizz buzz");
    } else if(isFizz){
        console.log(i + " fizz");
    } else if(isBuzz){
        console.log(i + " buzz");
    } else {
        console.log(i);
    }
}

*Note: The performance of the overall algorithm is greater than linear, as some() iterates over the arrays and is inside a for loop. Another approach is make isFizz = n => n%3 === 0 and isBuzz = n => n%5 === 0, and get linear time complexity.

you can do that...

const 
  fizz = [3, 6, 9, 12, 15, 18]
, buzz = [5, 10, 15, 20]
  ;
fizz.concat(buzz).sort((a,b)=>a-b).forEach((v,i,{[i+1]:n})=>
  {
  if (v!=n)
    console.log(v, !fizz.includes(v) ? 'buzz': buzz.includes(v) ? 'fizz buzz': 'fizz' ) 
  })
.as-console-wrapper {max-height: 100%!important;top:0}

Would you mind explaining it?
@KENAXION

The javascript language allows to use the chaining of methods and functions
so

fizz.concat(buzz).sort((a,b)=>a-b).forEach(...)

is equivalent to

let arr = fizz.concat(buzz) // adding fizz buzz into new array ( named arr )
arr.sort((a,b)=>a-b)
arr.forEach(...)

in this chainning :
Array.concat(...) return a new array
witch is used by arr.sort(...) and return a ref to the same array "sorted"
witch is used by arr.forEach(...)

the sort method use an Arrow function expressions

arr.sort((a,b)=>a-b)

is eq to

arr.sort( function(a,b) { return a-b } )

Array.forEach()also uses an Arrow function expression,
his internal function can accept 3 arguments:
~ the current loop element,
~ his index,
~ a reference to the original array (usefull in chainning operations; like here)

here the reference to the original array undergoes a transformation to return the value of the element i + 1, by the operation { [i + 1] : n }, in the variable named n (for next)

the story:
when the concatenated array of fizz and buzz is sorted, some elements are duplicated, and inevitably follow each other after sorting. Here, the way to ignore these duplicates is to check that the processed element is different from that of its next if (v != n).

then the console.log calls a double ternary test

!fizz.includes(v) ? 'buzz': buzz.includes(v) ? 'fizz buzz': 'fizz'

which can be summarized as follows:

if (!fizz.includes(v)      return 'buzz'
else if (buzz.includes(v)) return 'fizz buzz'
else                       return 'fizz'

doc links :
Array.concat(), Array.sort(), Array.forEach(), Conditional (ternary) operator, Array.includes()

if you really need to get intermediate values

const arrs =
  { fizz : Array.from({length: 6},(_,i)=>(i+1)*3)
  , buzz : Array.from({length: 4},(_,i)=>(i+1)*5)
  , zazz : [ 1, 3, 5, 8 ]
  };

Object.values(arrs)
  .reduce((sum,arr)=>sum.concat(arr),[])
  .sort((a,b)=>a-b)
  .forEach((v,i,{[i-1]:p})=>
  {
  for(let n=(p??0)+1;n<v;++n) console.log(n)
  if (v!=p)
    console.log(v, Object.keys(arrs).filter((nm)=>arrs[nm].includes(v)).join(' ') ) 
  })
.as-console-wrapper {max-height: 100%!important;top:0}

A Boolean one:

In the case of a simple equality test, Array.includes() is faster and preferable to Array.some()
and
a simple array ref if faster than a cascade of if

console.time('speed test')

const 
  fizz = [3, 6, 9, 12, 15, 18]
, buzz = [5, 10, 15, 20]
, txts = [ '', 'fizz', 'buzz', 'fizz buzz']
  ;
for (let i = 1; i <= 20; ++i ) 
  {
  let ref = fizz.includes(i) ? 1 : 0;
     ref |= buzz.includes(i) ? 2 : 0;
  console.log( i, txts[ref] )
  }

console.timeEnd('speed test')
.as-console-wrapper {max-height: 100%!important;top:0}

Last last one solution(4)

const arrs  =
  [ { fizz: [ 3, 6, 9, 12, 15, 18] }
  , { buzz: [ 5, 10, 15, 20 ] }
  , { zazz: [ 1, 3, 5, 8 ] }
  ]

showArrCom(arrs) 

function showArrCom(ArrList)
  {
  const
    arrN = ArrList.map(o=>Object.entries(o).reduce((r,[k,v])=>({n:k,i:0,a:v}),{}))
  , doLoop =_=> arrN.reduce((t,{a,i})=>t || (a[i]!=undefined), false)
    ;
  for (let indx=1; doLoop(); indx++ )
    {
    document.write( arrN.reduce((m,{n,a,i},x,t)=>
      {
      if (a[i]===indx) { m += `${n} `; t[x].i++ }
      return m
      }, `${indx} `) + '<br>')
    }
  }

I'm a bit confused by what you're trying to accomplish but I think I get it... you want to check if a given number is in one of the arrays right?

For that you can use .includes or .indexOf.

.includes will check if the value exists in the array, whereas .indexOf will give you the index of the value in the array or -1 if it doesn't exists.

Additionally, for the .forEach attempt you made. Firstly you need to create an array that will be the range of number from 1 to 20. Then, you apply the .forEach on the created range using the same logic you already built.

With that in mind you can also expand your code to add multiple arrays into play.

const fizz = [3, 6, 9, 12, 15, 18];
const buzz = [5, 10, 15, 20];

// create range of number from X to Y
const createRange = (FROM, TO) => [...Array(TO - FROM + 1).keys()].map(i => i + FROM);

const values = createRange(1, 20);

console.log('using includes...');
values.forEach(i => {
    const isFizz = fizz.includes(i);
    const isBuzz = buzz.includes(i);
    if(isFizz && isBuzz) console.log(i + " fizz buzz");
    else if(isFizz) console.log(i + " fizz");
    else if(isBuzz) console.log(i + " buzz");
    else console.log(i);
});

console.log('using indexOf...');
values.forEach(i => {
    const isFizz = fizz.indexOf(i) !== -1;
    const isBuzz = buzz.indexOf(i) !== -1;
    if(isFizz && isBuzz) console.log(i + " fizz buzz");
    else if(isFizz) console.log(i + " fizz");
    else if(isBuzz) console.log(i + " buzz");
    else console.log(i);
});


const arrs = {
  fizz: [3, 6, 9, 12, 15, 18],
  buzz: [5, 10, 15, 20],
  zazz: [1, 3, 5, 8],
}

console.log('with multiple input arrays');
values.forEach(i => {
  const output = [i];
  Object.keys(arrs).forEach(key => {
    if (arrs[key].includes(i)) output.push(key);
  });
  
  console.log(output.join(' '));
});

Related