noah.plus

JavaScriptでイミュータブルに配列を操作するメソッドまとめ

2018-08-10

Reactでアプリケーションを作っていくときなど、純粋な(副作用のない)関数を最大限使うことが推奨される。

純粋関数(Pure Function)であるためには、基本的に以下の条件を満たしておく必要がある。

  1. 関数は少なくとも1つの引数を持たなくてはならない
  2. 関数は1つの値、または1つの関数を返さなくてはならない
  3. 関数は引数を変化させてはいけない

3つ目の条件がなかなかのくせもので、意識せずにいると思わぬ副作用を起こしてしまうので注意が必要。特にJavaScriptの場合、配列を操作するときにはもとの配列を直接操作してしまっていないか意識する必要がある。

JavaScriptのArrayオブジェクトに属するメソッドには「破壊的な」メソッドが存在しており、「破壊的な」メソッドはもとの配列を直接操作(破壊)してしまう。一方で「非破壊的な」メソッドとは、配列を直接操作するのではなく、もとの配列のコピーに対して操作を行ってその結果を返す。

破壊的なメソッドと非破壊的なメソッドは混在していて見分けがつかない。これはもう覚えるしかないのでまとめる。

Arrayオブジェクトの破壊的なメソッドまとめ

これらのメソッドはもとの配列を直接変化させるので注意しなくてはいけない。

  • splice()
  • copyWithin()
  • fill()
  • pop()
  • push()
  • shift()
  • unshift()
  • reverse()
  • sort()

イミュータブルに配列を操作する

破壊的なメソッドを使わなくても配列の操作は問題なくできる。

要素の追加

スプレッド演算子を使う。破壊的なpush()unshift()は使わない。

const numbers = [1, 2, 3]

const newNumbers = [...numbers, 4]
console.log(newNumbers)
// [1, 2, 3, 4]

要素の削除

filter()を使う。破壊的なpop()shift()splice()は使わない。

filter(fnc [,that])

const numbers = [ "one", "two", "three"]

const newNumbers = numbers.filter(number => number !== "two")
console.log(newNumbers)
// ["one", "three"]

配列の加工

主に使うのはmap()reduce()の2つ。functionalなJavaScriptを書くためには、この2つをマスターすることが必須。

map(fnc [,that])

配列内の各要素を関数fncで順番に加工する。

var numbers = [1, 4, 9, 16];

const newNumbers = numbers.map(x => x * 2);
console.log(newNumbers);
// [2, 8, 18, 32]

配列の各要素を2倍に。

const numbers = [1, 2, 3, 4, 5]
const editNumber = (oldNumber, newNumber, arr) =>
	arr.map(number => (number === oldNumber) ? newNumber : number
)

const updatedNumbers = editNumber(3, 100, numbers)
console.log(updatedNumbers)
// [1, 2, 100, 4, 5]

配列内の特定の要素を変更する。条件分岐は三項演算子ですっきり。

reduce(fnc [,init])

initは初期値。隣接する2つの要素を左から右へ関数fncで処理し、単一の値にたたみ込む。

const numbers = [15, 24, 13, 35]

const max = numbers.reduce(
	(max, value) => (value > max) ? value : max,
    0
)

console.log('max', max);
// max 35

配列から最大値を取り出す。

const numbers = ["one", "one", "two", "three", "two"];
const distinctNumbers = numbers.reduce(
    (distinct, number) =>
		(distinct.indexOf(number) !== -1) ?
    		distinct :
    		[...distinct, number],
	[]
)

console.log(distinctNumbers)
// ["one", "two", "three"]

配列から重複した要素を取り除く。

破壊的なメソッドを使いたいとき

どうしても破壊的なメソッドを使いたいときがある。特にreverse()sort()など。

その場合はもとの配列を変えてしまわないように、スプレッド演算子でコピーを作ってから操作する。

const numbers = [1, 2, 3]

const newNumbers = [...numbers].reverse();
console.log(newNumbers);
// [3, 2, 1]

参考: functional non-destructive array sort


noah.plus