Pure JavaScript vs Lodash

この記事は、[Business Bank Group Developers Advent Calendar](https://qiita.com/advent-calendar/2018/bbg_developers) 8日目の記事です。

---

こんにちは、BBGの清水です。

[Lodash](https://lodash.com/docs/4.17.11)はリスト操作やオブジェクト操作に便利なライブラリです。
chainメソッドでeach, map, filterなどの処理を繋げて実装すると可読性も上がるためよく使用しています。

ただ、純粋なJavaScriptのメソッドの方がパフォーマンスが良いと聞いて気になったので比較してみました。
今回は、私がよく開発で使っていた処理を比較してみました。(今Angularで開発しているのでコードはtypescriptです)

### 事前データ
```typescript
let data1: { a: number, b: number[] }[] = [];
let data2: { a: number, b: number[] }[] = [];

Array.from({ length: 10000 }, (_) => {
  const num = Math.random();
  data1.push({ a: num, b: [1,2,3] });
  data2.push({ a: num, b: [1,2,3] });
});
```

### Pattern 1

```typescript:リスト内の特定の値をフィルタしたものをsummaryしたい
pureJS(data: { a: number, b: number[] }[]) {
  let sum = 0;
  const startMs = performance.now();

  data.map((item) => { return item.a })
    .filter((a) => a % 2)
    .forEach((o) => sum += o);

  return performance.now() - startMs;
}

lodash(data: { a: number, b: number[] }[]) {
  const startMs = performance.now();

  _.chain(data).map('a')
    .filter((a) => a % 2)
    .sum().value();

  return performance.now() - startMs;
}
```

```typescript:1000回呼び出した平均値で比較してみる
let result1 = [];
let result2 = [];
Array.from({ length: 1000 }, (_) => {
  result1.push(this.pureJS(data1));
  result2.push(this.lodash(data2));
});
console.log('PureJS(ms): ' + _.sum(result1) / 1000);
console.log('Lodash(ms): ' + _.sum(result2) / 1000);

// PureJS(ms): 0.6674000000014058
// Lodash(ms): 0.8038999999985245
```

### Pattern 2
```typescript:リストに処理を加えた後特定の値で並び替えをしたい
pureJS(data: { a: number, b: number[] }[]) {
  const startMs = performance.now();
  data.forEach((item) => item.a *= 3);
  data.sort((l1, l2) => { return (l1.a > l2.a) ? 1 : -1 });
  return performance.now() - startMs;
}

lodash(data: { a: number, b: number[] }[]) {
  const startMs = performance.now();
  _.chain(data).each((item) => item.a *= 3)
    .sortBy('a').value();
  return performance.now() - startMs;
}
```
```typescript:1000回呼び出した平均値で比較してみる
let result1 = [];
let result2 = [];
Array.from({ length: 1000 }, (_) => {
  result1.push(this.pureJS(data1));
  result2.push(this.lodash(data2));
});
console.log('PureJS(ms): ' + _.sum(result1) / 1000);
console.log('Lodash(ms): ' + _.sum(result2) / 1000);

// PureJS(ms): 0.001800000000912405
// Lodash(ms): 0.014900000001944136
```

### Pattern 3
```typescript:二次元リストを一次元リストにしたい
pureJS(data: { a: number, b: number[] }[]) {
  const startMs = performance.now();
  data.map((o) => { return o.b })
    .reduce((a, b) => a.concat(b), []);
  return performance.now() - startMs;
}

lodash(data: { a: number, b: number[] }[]) {
  const startMs = performance.now();
  _.chain(data).map('b')
    .flatten().value();
  return performance.now() - startMs;
}
```
```typescript:10回呼び出した平均値で比較してみる
let result1 = [];
let result2 = [];
Array.from({ length: 10 }, (_) => {
  result1.push(this.pureJS(data1));
  result2.push(this.lodash(data2));
});
console.log('PureJS(ms): ' + _.sum(result1) / 10);
console.log('Lodash(ms): ' + _.sum(result2) / 10);

// PureJS(ms): 239.27999999996172
// Lodash(ms): 2.710000000070431
```



### まとめ
eachやmapなど単純な処理を行いたい時は純粋なJavaScriptの方がパフォーマンスがいいので、わざわざLodashを使用しなくてもいいかなと思いました。
ただ、flattenなどの純粋JavaScriptで実現しようとすると処理がネストになったりするような、ちょっと複雑なことをしたい時はLodashの方が早いケースもあり、かつ可読性も上がるなと思いました。
うまく使い分けて行きたいです！



明日は、寿司好きフロントエンジニア[金子さん](https://qiita.com/tronperidot)です！
お願いします🍣