# Create a for loop that iterates up to 100 while outputting "fizz" at multiples of 3, "buzz" at multiples of 5 and "fizzbuzz" at multiples of 3 and 5.
> FizzBuzz 是一個經典的編程面試考題。
> 建立一個for迴圈,印出從1~100,若為"3的倍數顯示 Fizz "、若為"5的倍數顯示 Buzz "、若同為3和5的倍數顯示 FizzBuzz"
## 利用餘數的觀念解決問題:
> 在算術中,當兩個整數相除的結果不能以整數商表示時,餘數便是其「餘留下的量」。**當餘數為零時,被稱為整除。**
> 程式執行時,順序是一行一行下來。所以最嚴格的條件,應該要放在前面優先執行。**當 i 對同為3和5的倍數 取餘數,餘數為零時,則顯示"FizzBuzz"** 應寫在 if-else 的判斷式的前面。
## 各式各樣的解法:
> 題目建議在面試時選擇清楚好理解的方式,程式比較長也沒關係。
### 較常見的if else:
#### 版本 1:
```javascript=
const n = 100;
for (var i=1; i <= n; i++){
if (i % 15 == 0)
console.log("FizzBuzz");
else if (i % 3 == 0)
console.log("Fizz");
else if (i % 5 == 0)
console.log("Buzz");
else
console.log(i);
}
```
:sunflower:**程式步驟:**
1. 先令某數為 i
1. 當 i 對 同為3和5的倍數 取餘數,餘數為零時,則顯示"FizzBuzz"
1. 當 i 對 3 取餘數,餘數為零時,則顯示"Fizz"
1. 當 i 對 5 取餘數,餘數為零時,則顯示"Buzz"
:::info
答案印出來是對的!
但不建議使用(3X5)15的倍數去解題,因為==3的倍數印Fizz==、==5的倍數印Buzz==,這兩個條件是獨立的,直接相乘的話程式的可讀性會降低。
如果多了其他的條件,就要多寫。(eg. 7的倍數印woof, i%7、i%21、i%35、i%105)
:::
#### 版本1 修正:
```javascript=
const n = 100;
for (var i=1; i <= n; i++){
if (i % 3 == 0 &&i % 5 == 0 )
console.log("FizzBuzz");
else if (i % 3 == 0)
console.log("Fizz");
else if (i % 5 == 0)
console.log("Buzz");
else
console.log(i);
}
```
<hr>
#### 版本 2:
```javascript=
const n = 100;
for(let i = 1; i <= n; i++){
let result = '';
if(i%3 === 0){
result += 'Fizz'
}
if(i%5 === 0){
result += 'Buzz'
}
if(i%7 === 0){
result += '777'
} //直接加一個if判斷解決版本1的問題
if(result.length >0){
console.log(result);
}
else {
console.log(i);
}
}
```
有新增的條件只要多增加一組if判斷就可以,原理是用空的字串 result ,遇到符合條件的就疊加上去。
<hr>
<hr>
### 利用三元運算子:
```
condition ? exprIfTrue : exprIfFalse
```
[Ternary(Conditional)operator 三元(條件) 運算子](https://hackmd.io/@pikachuchu/B1S8bNVet)
#### 版本1 :
```javascript=
for (var i = 1; i <= 100; i++) {
str = (i % 5 == 0 && i % 3 == 0) ? "FizzBuzz" : (i % 3 == 0 ? "Fizz" : (i % 5 == 0) ? "Buzz" : i);
console.log(str);
}
```
:sunflower:**詳細步驟:**
1. 建立一個For迴圈從1數到100
1. 當 i **對 同為3和5的倍數 取餘數**。餘數為0時,執行"FizzBuzz",餘數不為0,執行``(i % 3 == 0 ? "Fizz" : (i % 5 == 0) ? "Buzz" : i)``
1. 當 i **對 3 取餘數**。餘數為0時,執行`"Fizz"`,餘數不為0,執行``(i % 5 == 0)? "Buzz" : i``
1. 當 i **對 5 取餘數**,餘數為0時,執行`"Buzz"`,餘數不為0,執行`i`。
<hr>
#### 版本2 :
```javascript=
for (var i = 1; i <= 100; i++) {
var f = i % 3 == 0, b = i % 5 == 0;
console.log(f ?( b ? "FizzBuzz" : "Fizz" ):( b ? "Buzz" : i));
}
```
:sunflower:**詳細步驟:**
==當程式跑 i=15 的情況:==
1. 建立一個For迴圈從1數到100
1. `f ?( b ? "FizzBuzz" : "Fizz" ):( b ? "Buzz" : i)`,餘數為0時,執行`( b ? "FizzBuzz" : "Fizz" )`,~~餘數不為0,執行`( b ? "Buzz" : i)`~~。
3. `b ? "FizzBuzz" : "Fizz"`,餘數為0時,執行`"FizzBuzz"`,~~餘數不為0,執行`"Fizz"`。~~
<hr>
#### 版本3 : 目前看到最簡潔的寫法
```javascript=
for(i=0;i<100;)console.log((++i%3?'':'Fizz')+(i%5?'':'Buzz')||i)
```
:::info
一般情況下沒問題,但在“嚴格模式”(use strict)下若省去`let`,會跑錯。*ReferenceError: i is not defined*
:::
:sunflower:**運用到的觀念:**
| Operator | Usage | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| 增加 (++) | 一元運算子。 將運算元增加1。假如使用在運算元之前 ``(++x)``,運算元會回傳增加1後的值;假如使用在運算元之後。 ``(x++)``, 會回傳運算元加1前的值。 | 假如 x是 3,那 ``++x`` 將把 x 設定為 4 並回傳 4,而 ``x++`` 會回傳 3 , 接著才把 x 設定為 4。 |
| OR (ll) | 運算式1 ll 運算式2 | 假如 運算式1 可以被轉換成 true的話,回傳 運算式1; 否則,回傳 運算式2。 因此,**ll在 兩個運算元有任一個是True 時就會回傳 True,否則回傳 false**。 |
| Falsy values |  | **false values以外的都是truthy values** |
:sunflower:**詳細步驟:**
==程式跑第一次 `i=0` 的情況:==
**先來處理console.log前段的部分:**
1. `++i%3 ? '' : 'Fizz'` ------------> 使用到三元運算子的觀念 `condition ? value if true : value if false`。 如果為 true執行 `''` ,false執行`Fizz`。
2. `++i%3` ------------> 運用到增加運算符 ++的觀念。
我們第一個執行的i為1 (++i使0變1)。 1%3 會得到 1,運算式會變成 `1 ? '' : 'fizz'`,1為truthy value,

所以會變成`true ? '' : 'fizz'`,最終得出`''`。
3. 整段程式變成
```javascript=
for(let i=0;i<100;)
console.log(
( '' ) + ( i%5 ? '' : 'buzz' ) || i
)
```
**接著處理console.log後段的部分:**
1. `i%5 ? '' : 'buzz'` ----------------> 一樣使用到三元運算子的觀念 `condition ? value if true : value if false`。 如果為 true執行 '' ,flase執行buzz。
2. `i%5` 這邊的i帶入1, 1%5 會得到1,運算式會變成 `1 ? '' : 'buzz'`,1為truthy value

所以會變成`true ? '' : 'buzz'`,最終得出`''`。
程式經過了上面兩段步驟被簡化成
```javascript=
console.log( '' + '' || i )
```
由於空字串加空字串仍會是空字串,程式會再進一步簡化成
```javascript=
console.log( '' || i )
```
我們會運用到OR(||)的觀念,由於空字串是 JavaScript 中的七個falsy values之一,程式會跳過空字串執行下一個運算元(operand),這邊指的是i。
```javascript=
console.log(i) //1
```
分開操作看看結果:
https://codepen.io/natalia0604/pen/PojqpQP
==當程式跑 `i=14` 的情況:==
```javascript=
for(let i=0;i<100;)
console.log(
( ++i%3 ? '' : 'fizz' ) + ( i%5 ? '' : 'buzz' ) || i
)
```
* `++i%3` 這邊的i會變成15, 運算式會變成`15 % 3 ? '' : 'fizz'`,`15%3` 會得到0,
* `i%5` 這邊的i帶入15, 運算式會變成`15 % 5 ? '' : 'buzz'`,`15%5` 會得到0。

0為falsy value,所以程式會變成
```javascript=
console.log( 'fizz' + 'buzz' || i )
```
再簡化成
```javascript=
console.log( 'fizzbuzz' || i )
```
由於非空字符串('fizzbuzz')是 truthy value,OR(||) 將短路並返回 'fizzbuzz'
```javascript=
console.log('fizzbuzz')
```
<hr>
<hr>
### 搭配forEach
``` javascript=
var multipliers = [
[3, "Fizz"],
[5, "Buzz"]
]
for (let i = 1; i <= 100; i++) {
let output = "";
multipliers.forEach(function(item) {
if (i % item[0] === 0) {
output += item[1];
}
});
console.log(output || i);
}
```
:sunflower:**詳細步驟:**
* `item[0]`指的是multipers的3、5,所以每當`i++`時都會讓 i對3和5取餘數。
* 如果`i%item[0]=== 0`成立就會將`item[1]`的字串(指的是"Fizz"和"Buzz"
)加入`output`裡,接著執行 `console.log(output || i)`
* 依照OR(||)的規則,`output`不為空字串時,會回傳`console.log(output)`。
**分開操作看看結果:**
==當程式跑 `i=15` 的情況:==
```javascript=
var multipliers = [
[3, "Fizz"],
[5, "Buzz"]
]
for (let i = 15; i <= 15; i++) {
let output = "";
multipliers.forEach(function(item) {
console.log(item[0]);
if (i % item[0] === 0) {
output += item[1];
}
});
console.log(output || i);
}
```
<hr>
<hr>
### 搭配map
#### 版本1 :
```javascript=
[...Array(100).keys()].map(n => ++n).map(n => console.log(`${n % 3 ? '' : 'Fizz'}${n % 5 ? '' : 'Buzz'}` || n));
```
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| Template literals (樣板字面值) | ${expression} | 可以放入變數或任何的表達式。 eg.( ${3+4} 、 \${2*(numA + numB)} ) 。 當 \${'one'}${'two'} 可以合併字串,'onetwo'。|
```javascript=
// ${}${}
let a = 'apple';
let b = 'banana';
console.log(`${a}${b}`); //"applebanana"
```
:sunflower:**詳細步驟:**
==簡化成跑到15的版本==
```javascript=
[...Array(15).keys()].map(n => ++n).map(n => console.log(`${n % 3 ? '' : 'Fizz'}${n % 5 ? '' : 'Buzz'}` || n));
```
* `[...Array(15).keys()].map(n => ++n)` ------------> 會得到一個從1-15的陣[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
* 用map()去遍歷array裡的每一個元素,跑console.log裡的判斷式。
```javascript=
// 當n=1
console.log(`${n % 3 ? '' : 'Fizz'}${n % 5 ? '' : 'Buzz'}` || n)
會簡化成
console.log(${''} || n)
再簡化成
console.log(n)
//當n=3
console.log(`${n % 3 ? '' : 'Fizz'}${n % 5 ? '' : 'Buzz'}` || n)
會簡化成
console.log(${'Fizz'}${''} || n)
再簡化成
console.log('Fizz')
//當n=15
console.log(`${n % 3 ? '' : 'Fizz'}${n % 5 ? '' : 'Buzz'}` || n)
會簡化成
console.log(${'Fizz'}${'Buzz'} || n)
再簡化成
console.log('FizzBuzz')
```
<hr>
#### 版本 2:
```javascript=
a=[,,,'Fizz',,'Buzz']
Array.from(' '.repeat(101)).map((b,i)=>i&&console.log(
a.map((c,j)=>i%j?'':c).join('')||i
))
```
:sunflower:**詳細步驟:**
先看前半段
```javascript=
a= [ , , ,'Fizz', ,'Buzz']
index[0,1,2, 3 ,4, 5 ]
Array.from() 可以產生array的方法
Array.from(' '.repeat(101)) 會印出101個空字串的array
Array.from(' '.repeat(101)).map((b,i)=>i 會印出0-100的array
```
再來看console.log()
==當程式跑 `i=15` 的情況:==
```javascript=
// 當 i=15
a.map((value,index)=>i%index?'':value).join('')||i
a.map(("",0)=>15%0?'':"") //''
a.map(("",1)=>15%1?'':"") //''
a.map(("",2)=>15%2?'':"") //''
a.map(('Fizz',3)=>15%3?'':'Fizz') //'Fizz'
a.map(("",4)=>15%4?'':"") //''
a.map(('Buzz',5)=>15%5?'':'Buzz') //'Buzz'
join('') ------------> 'FizzBuzz'
a.map('FizzBuzz'||i) //'FizzBuzz'
```
<hr>
#### 版本 3:
```javascript=
console.log(Array(100).fill(1).map(function(val, index) {
index++;
if (index % 15 == 0){return "FizzBuzz";}
if (index % 3 == 0){return "Fizz";}
if (index % 5 == 0){return "Buzz";}
return index;
}).join('\n'))
```
:sunflower:**詳細步驟:**
==簡化成跑到15的版本==
* `Array(15).fill(1)` -----------------> 會得到[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
* `Array(15).fill(1).map()`
```
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
index[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ]
```
```javascript=
function(val, index) {
index++;
if (index % 3 == 0 && index % 5 == 0){return "FizzBuzz";}
if (index % 3 == 0){return "Fizz";}
if (index % 5 == 0){return "Buzz";}
return index;
}).join('\n'))
```

[What is Array.apply actually doing](https://stackoverflow.com/questions/25512771/what-is-array-apply-actually-doing)
[codepen](https://codepen.io/phoebe4561/pen/yLXLQEM?editors=0012)
## 小結:
可以從 考慮**拓展性、效率**的角度去進化你的fizzbuzz程式。
還有很多有創意,上面沒提到的解法,可以點進去慢慢看
[FizzBuzz JavaScript solution](https://gist.github.com/jaysonrowe/1592432)
## 參考資料:
[JavaScript — Breaking Down The Shortest Possible FizzBuzz Answer](https://codeburst.io/javascript-breaking-down-the-shortest-possible-fizzbuzz-answer-94a0ad9d128a)
[簡單的 FizzBuzz 藏有 深度(google 面試題)](https://bear-1111.medium.com/%E7%B0%A1%E5%96%AE%E7%9A%84-fizzbuzz-%E8%97%8F%E6%9C%89-%E6%B7%B1%E5%BA%A6-google-%E9%9D%A2%E8%A9%A6%E9%A1%8C-f5e66e3a97be)
https://stackoverflow.com/questions/25512771/what-is-array-apply-actually-doing