[Javascript]まちがえながらも感覚的に実装できるまで昇華していく[前編]


#目的
- 関数の実装を繰り返し実施し、実装までの思考法を身につける。
- 関数の概念を頭で理解するだけでなく、感覚的に操作できるまで昇華する。
- 英語でテクニカルな内容を理解する。


#今回やること
lodashのライブラリを実装しながら学んだことをアウトプットしていきます。

#_.zipObject([props=[]], [values=[]])
配列を２つ受け取り、オブジェクトにして返す。

```
_.zipObject(['a', 'b'], [1, 2]);
// => { 'a': 1, 'b': 2 }
```

###実装
でけた。特に問題なし

```
zipObject = (props = [], values = []) => {
    let obj = {};
    for (let i = 0; i < props.length; i++) {
        obj[props[i]] = values[i]
    }
    return obj
}

```


#_.without(array, [values])
配列と値を受け取り、値を配列から取り除く。

```
_.without([2, 1, 2, 3], 1, 2);
// => [3]
```

###詰まった。
配列と値を比較し取り除いた配列を抽出するため、forとfilterネストして使おうとした。
しかし、今回のようにvalueが複数あると、filterは「1を除く配列」と「２を除く配列」を別々に作る。

filterのコールバック関数のreturn部分を工夫する方法（＆＆）を考えた。
ただ、複数引数をとるときの実装ができなかった。（もし、わかる人はそっと教えてください。）

->引数の数によらず処理ができるようにしたい。が実装方法を思いつかなかった

###学び
配列中の”複数の”の値を削除したいとき、配列に対してではなく、**複数の値に対して有無**を考える。


配列に対し、除きたい要素と比較しひとつずつ除いていく考え方ではなく、
除きたい要素群に対し、配列の要素と比較しひとつずつ加えていく考え方。

###実装

```
without = (array = [], ...values) => {
    let newArray = [];
    for (let i = 0; i < array.length; i++) {
        const element = array[i];
        if (!values.includes(element)) {
            newArray.push(element);
        }
    }
    return newArray;
}
```


##_.uniq(array)
配列内の重複した要素を取り除いて配列を返す。

```
_.uniq([2,1,2]);
// => [2,1]
```

###また詰まった。

```
uniq = (array = []) => {

    let newarray = array.reduce((result = [], item) => {
        console.log(Array.isArray(result)) //1回目false,2回目true
         if (!result.includes(item)) {
             result.push(item)
         }
    })
    return newarray
}


```


is not function
恐らくreduce1回目でArray.isArray(result)=falseのため。
　
->reduceで第３引数がないときの理解があまかった。

###学び

**reduceの第3引数がなければ、reduceに渡した最初の値はコールバック関数で処理されない。**

reduceで、第３引数がない場合、array[0]は、第3引数に格納、
array[1]以降、reduceのコールバック関数が実行され、第3引数に格納。

```
 _.reduce = (collection, iterator, accumulator) => {
    let i =0
    _.each(collection, (value, index, collection) => {
      if (accumulator === undefined && i==0) {
        accumulator = value;
        i++
      } else {
        accumulator = iterator(accumulator, value, index, collection);
      }
    });
    return accumulator
  };
```



つまり、[2,1,2]の最初の2は、if (accumulator === undefined && i==0)に入っている。


###実装vol.1

reduceは仕様的に厳しいのでforeachで実装した。

```
uniq = (array = []) => {
    let result = [];
    array.forEach((item) => {
        if (!result.includes(item)) {
            result.push(item)
        }
    })
    return result;
}

```


##@ttatsfさんにそっとご指導いただきました!ありがとうございます!（2020/7/3)

reduceは仕様的に厳しいと発言してしまいましたが前言撤回です。reduceでもできました。

```
const uniq = array =>
    array.reduce(
        (result, value) => result.includes(value) ? result
            : [...result, value]
        , []
    )
```

###学びvol.2
問題は、reduceだと、最初の要素が格納されたresultは配列ではないため、配列操作ができず、is not functionと表示されていたことでした。

理解したことは、以下２点です。
 
reduceの最初の要素を取り出して、2回目以降配列として操作したいときは、**reduce第3引数に[]を入れる。**
　->自分が作った関数で、resultのデフォルト引数が効いていない理由は解明中。
**pushは、スプレッド演算子でも対応可能。**

なお、今風の書き方も教えていただきました。（ありがとうございます）
これはまだ理解できていないため、残課題です。


##_.takeRight(array, [n=1])

第1引数の配列を、第2引数のn番目から最後までにして取り出す。

```
console.log(takeRight([1, 2, 3]));
// // => [3]

console.log(takeRight([1, 2, 3], 2));
// // => [2, 3]

console.log(takeRight([1, 2, 3], 5));
// // => [1, 2, 3]

console.log(takeRight([1, 2, 3], 0));
// => []
```

###ちょっと忘れた。

sliceの仕様忘れた。

```
let num = [1,2,3]
console.log(num.slice(2,3))
=> [3]
console.log(num.slice(,3))
=> Error
console.log(num.slice())
=> [1,2,3]
```

参考）https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

[1,2,3].slice（start,end)の場合、**start  <  i  <
 end**　のイメージ
引数がない場合は、**すべて**返す。

###実装

```
takeRight = (array, n) => {
    let result = []
    if (n === undefined) {
        //array.lengthのひとつ前にしたい。
        result = array.slice(array.length - 1)
    }
    if (n >= 1 && n < array.length) {
        //n<=i<=arraylength-1にしたい。sliceはstart<i<endなのでn-1<i<arraylength
        result = array.slice(n - 1, array.length)
    } if (n >= array.length) {
        result = array.slice()
    }
    return result;
}
```



##_.tail(array)
第1引数の配列の最初の要素を取り除き、新しい配列を
返す。

```
_.tail([1, 2, 3]);
=> [2, 3]
```

###実装
特に問題なし。

```
tail = (array = []) => {
    let tailarray = array.filter((value, index) => index !== 0)
    return tailarray;
}

```

##_.slice(array, [start=0], [end=array.length])
さっきつかっちゃったけどw

```
let num = [1,2,3]
console.log(num.slice(2,3))
=> [3]
console.log(num.slice(,3))
=> Error
console.log(num.slice())
=> [1,2,3]
```

###実装
特に問題なし

```
slice = (array, start, end) => {
    let sliceArray = [];
    for (let i = start; i <= end; i++) {
        sliceArray.push(array[i])
    }
    return sliceArray;
}
```


##_.nth(array, [n=0])
array[n]の要素を取り出す。

```
var array = ['a', 'b', 'c', 'd'];
console.log(nth(array, 1));
// => 'b'
console.log(nth(array, -2));
// => 'c';
```

###学び
**インデックスにマイナスは使えない。**
※ただし、計算式の中では可。

###実装
特に問題なし

```
nth = (array, n) => {
    if (n < 0) {
        //array.length + (-number)
        return array[array.length + n]
    } else {
        return array[n]
    }
}

```


何かお気づきの点がございましたらぜひコメントください・・！