Ramda로 불변적으로 배열 다루기

이 글은 함수형 프로그래밍 시리즈 'Thinking in Ramda'의 일곱 번째 편입니다. 지난 Part 6에서는 객체를 함수적이고 불변적인 방식으로 다루는 방법을 살펴보았습니다. 이번에는 배열을 동일한 원칙으로 다루는 방법을 알아보겠습니다.

배열 요소 읽기

Part 6에서 객체 속성 읽기를 위한 prop, pick, has 등의 Ramda 함수를 배웠습니다. 배열에는 이에 대응하는 더 많은 메서드가 있습니다. prop의 배열 버전은 nth, pickslice, hascontains입니다.

const nums = [10, 20, 30, 40, 50, 60];

nth(3, nums);   // => 40  (0부터 시작)
nth(-2, nums);  // => 50 (음수는 뒤에서부터)
slice(2, 5, nums); // => [30, 40, 50]
contains(20, nums); // => true

slice는 첫 번째 인덱스(0부터 시작)부터 두 번째 인덱스 직전까지의 하위 배열을 반환합니다.

첫 번째(nth(0))와 마지막(nth(-1)) 요소에 자주 접근하므로 Ramda는 headlast 같은 단축 함수를 제공합니다. 또한 첫 요소를 제외한 나머지(tail), 마지막 요소를 제외한 나머지(init), 처음 N개(take(N)), 마지막 N개(takeLast(N))를 가져오는 함수도 있습니다.

const nums = [10, 20, 30, 40, 50, 60];

head(nums);        // => 10
tail(nums);        // => [20, 30, 40, 50, 60]
last(nums);        // => 60
init(nums);        // => [10, 20, 30, 40, 50]
take(3, nums);     // => [10, 20, 30]
takeLast(3, nums); // => [40, 50, 60]

배열 요소 추가, 수정, 삭제

객체에서는 assoc, dissoc, omit으로 속성을 추가/수정/삭제했습니다. 배열은 순서가 있는 자료구조이므로 assoc와 유사한 여러 메서드가 있습니다. 가장 일반적인 것은 insertupdate이며, 시작이나 끝에 요소를 추가하는 appendprepend도 있습니다.

const nums = [10, 20, 30, 40, 50, 60];

insert(3, 35, nums);  // => [10, 20, 30, 35, 40, 50, 60]
append(70, nums);     // => [10, 20, 30, 40, 50, 60, 70]
prepend(0, nums);     // => [0, 10, 20, 30, 40, 50, 60]
update(1, 15, nums);  // => [10, 15, 30, 40, 50, 60]

두 객체를 합칠 때 merge를 썼듯이, 배열에는 concat을 사용합니다.

const nums = [10, 20, 30, 40, 50, 60];

concat(nums, [70, 80, 90]); // => [10, 20, 30, 40, 50, 60, 70, 80, 90]

두 번째 배열이 첫 번째에 추가됩니다. 파이프라인에서 쓸 때는 의도와 다를 수 있으므로 concatAfter 헬퍼를 정의하는 것이 유용합니다.

const concatAfter = flip(concat);

요소 삭제는 여러 방법이 있습니다. remove는 인덱스로, without은 값으로 삭제합니다. dropdropLast는 각각 앞이나 뒤에서 요소를 제거합니다.

const nums = [10, 20, 30, 40, 50, 60];

remove(2, 3, nums);      // => [10, 20, 60]
without([30, 40, 50], nums); // => [10, 20, 60]
drop(3, nums);           // => [40, 50, 60]
dropLast(3, nums);       // => [10, 20, 30]

remove는 인덱스와 개수를 받지만 slice는 두 인덱스를 받습니다. 이 차이를 기억해야 합니다.

요소 변환하기

객체처럼 배열 요소에도 함수를 적용해 값을 변경할 수 있습니다. adjust는 객체의 evolve와 유사하지만 단일 요소에만 작동합니다.

const nums = [10, 20, 30, 40, 50, 60];

// 수동 방식
update(2, multiply(10, nth(2, nums)), nums); // => [10, 20, 300, 40, 50, 60]

// adjust 사용
adjust(2, multiply(10), nums); // => [10, 20, 300, 40, 50, 60]

이제 배열과 객체를 선언적이고 불변적으로 다루는 도구를 갖추었습니다. 작은 함수형 블록으로 프로그램을 조립하면서 데이터 구조를 변경하지 않고 필요한 작업을 수행할 수 있습니다.

다음 글에서는 더 일반적인 도구인 lens를 알아보겠습니다. 객체 속성과 배열 요소를 읽고 수정하고 변환하는 통합된 방법을 제공합니다.

태그: Ramda 함수형 프로그래밍 불변성 배열 자바스크립트

6월 8일 04:04에 게시됨