0

What I am trying to achieve is a pattern-matching version of FizzBuzz. Similar to the one available in Swift just using JavaScript.

If a number is divisible by 3 console.log Fizz and if by 5 then Buzz if by both FizzBuzz if neither then log the number. The challenge I am facing is this:

function fizzBuzz(numbers) {
  for (number in numbers) {
    let div3 = number % 3 == 0 ? true : false;
    let div5 = number % 5 == 0 ? true : false;
     
    switch ([div3, div5]) {
      case [true, true]:
        console.log("FizzBuzz");
        break;
      case [true, false]:
        console.log("Fizz");
        break;
      case [false, true]:
        console.log("Buzz");
        break;
      case [false, false]: 
        console.log(number);
            break;
    }
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);

this does not work I tried without the square brackets but then only the first variable gets evaluated and the other one is ignored. Could someone shed some light onto this for me? In Swift this is very simple and works flawlessly I am trying to achieve the same in JS.

I tried this but like mentioned above only evaluates the first variable in the brackets which is div3, div5 gets ignored completely:

function fizzBuzz(numbers) {
  for (number in numbers) {
    let div3 = number % 3 == 0 ? true : false;
    let div5 = number % 5 == 0 ? true : false;
     
    switch (div3, div5) {
      case (true, true):
        console.log("FizzBuzz");
        break;
      case (true, false):
        console.log("Fizz");
        break;
      case (false, true):
        console.log("Buzz");
        break;
      case (false, false): 
        console.log(number);
            break;
    }
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);

Is there even a syntax like this which I am trying to achieve in JS?

AD Progress
  • 4,190
  • 1
  • 14
  • 33
  • JavaScript doesn't compare arrays by comparing the contents. – Barmar Oct 30 '20 at 20:57
  • Yes but in this context it is a tuple not an array. The only reference I found is that JS tuples look like arrays – AD Progress Oct 30 '20 at 20:59
  • 1
    What angle brackets? `[]` are square brackets, `<>` are angle brackets. – Barmar Oct 30 '20 at 21:00
  • 2
    JavaScript doesn't have tuples. You can use an array as a tuple, but it's still an array. – Barmar Oct 30 '20 at 21:00
  • @Barmar good point updated the question – AD Progress Oct 30 '20 at 21:03
  • @Barmar Could you reopen it as it is not what I am trying to do I don't want to compare an array I want a similar result as I explained in the question I have updated it could you reopen it please? – AD Progress Oct 30 '20 at 21:06
  • Do you have to use a switch statement? `if (div3 && div5) {console.log("FizzBuzz");} else if (div3) {console.log("Fizz");} else if (div5) {console.log("Buzz");} else {console.log(number);}` – mykaf Oct 30 '20 at 21:16
  • @user1599011 They're specifically trying to do it using pattern matching, not some other algorithm. – Barmar Oct 30 '20 at 21:19
  • @Barmar, I don't see how the original is pattern matching in the first place. What pattern is being matched? – mykaf Nov 02 '20 at 17:55
  • It's matching a pair of values in a tuple. @user1599011 – Barmar Nov 02 '20 at 17:56

3 Answers3

1

Not sure if this is a silly way to do it:

const case_signature = (...args) => JSON.stringify(args);

function fizzBuzz(numbers) {
  for (number in numbers) {
    let div3 = number % 3 === 0;
    let div5 = number % 5 === 0;
     
    switch (case_signature(div3, div5)) {
      case case_signature(true, true):
        console.log("FizzBuzz");
        break;
      case case_signature(true, false):
        console.log("Fizz");
        break;
      case case_signature(false, true):
        console.log("Buzz");
        break;
      case case_signature(false, false): 
        console.log(number);
            break;
    }
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);

A different way (but not sure it meets your requirements):

const multi_mod = (number, ...divisors) =>
  number % divisors.reduce((acc, div) => acc = acc * div, 1);

function fizzBuzz(numbers) {
  for (number in numbers) {
    switch (0) {
      case multi_mod(number, 3, 5):
        console.log("FizzBuzz");
        break;
      case multi_mod(number, 3):
        console.log("Fizz");
        break;
      case multi_mod(number, 5):
        console.log("Buzz");
        break;
      default: 
        console.log(number);
    }
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);

I don't really know what a tuple is, but maybe this is a bit more like your original question:

// array_equal borrowed from https://stackoverflow.com/a/19746771/14454939
const array_equal = (array1, array2) =>
  array1.length === array2.length && array1.every((value, index) => value === array2[index]);

const switch_tuple = (compare) => (...cases) => {
  for (let i = 0; i < cases.length; i++) {
    const result = array_equal(compare, cases[i][0]);
    if (result) {
      cases[i][1]();
      return result;
    }
  }
};

function fizzBuzz(numbers) {
  for (number in numbers) {
    let div3 = number % 3 == 0 ? true : false;
    let div5 = number % 5 == 0 ? true : false;

    switch_tuple([div3, div5])(
      [[true, true],
        () => console.log("FizzBuzz")
      ],
      [[true, false],
        () => console.log("Fizz")
      ],
      [[false, true],
        () => console.log("Buzz")
      ],
      [[false, false],
        () => console.log(number)
      ],
    )
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);
Ben Stephens
  • 3,303
  • 1
  • 4
  • 8
  • Could you please explain how this line works? const case_signature = (...args) => JSON.stringify(args); – AD Progress Oct 30 '20 at 22:34
  • 1
    I'd guess that arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions and rest parameters: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters are the unfamiliar parts of this. It could be rewritten to `function case_signature() { return JSON.stringify(arguments); };` (which would make use of the arguments object: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments ). – Ben Stephens Oct 31 '20 at 07:54
  • Thanks @Ben_Stephens for your input. It all helped a lot. – AD Progress Nov 02 '20 at 18:30
1

switch/case compares values using the === operator. This compares arrays by identity, not by contents, so you can't use it with array literals as you've tried.

JavaScript doesn't have tuples. div3, div5 uses the comma operator, which simply evaluates it to the value of div5.

If you want something like your coding pattern, I think the best you can do is use JSON.

function fizzBuzz(numbers) {
  for (number in numbers) {
    let div3 = number % 3 == 0 ? true : false;
    let div5 = number % 5 == 0 ? true : false;
     
    switch (JSON.stringify([div3, div5])) {
      case '[true,true]':
        console.log("FizzBuzz");
        break;
      case '[true,false]':
        console.log("Fizz");
        break;
      case '[false,true]':
        console.log("Buzz");
        break;
      case '[false,false]': 
        console.log(number);
            break;
    }
  }
}

var array = [...Array(100).keys()];

fizzBuzz(array);
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks. This is exactly what I was looking for, clear and readable. I hope that this type of pattern matching will eventually be included in JS so that we don't have to do workarounds. Have a fantastic day and Thanks once again. – AD Progress Oct 30 '20 at 22:33
  • 1
    I don't know what future plans are, but this seems very much outside the style of JS. – Barmar Oct 30 '20 at 22:41
0

You could also turn the arrays of booleans into a number, which can then be used to switch on:

  const pattern = (...args) => args.reduce((s, n, i) => s + n << i, 0);

  switch(pattern(true, false)) {
     case pattern(false, false):
        //...
  }
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151