객체를 이터러블 프로그래밍으로 다루는 방법에 대해 알아본다. 먼저 객체는 key, value의 쌍으로 이루어진 데이터 구조를 의미한다. 아래와 같은 객체가 있다고 하자

const obj1 = { a: 1, b: 2, c: 3 };

이를 배열화 할 수 있는 가장 간단한 방법은 Object.entries를 사용해 배열로 변환하는 것이다.

var arr = Object.entries(obj1); // [["a", 1], ["b", 2], ["c", 3]]
console.log(arr[Symbol.iterator]); // values(){ [native code] }
console.log(arr[Symbol.iterator]()); // Array Iterator {}

위처럼 만들어 배열에 Symbol.iterator를 적용하면 이터러블 프로그래밍을 구현할 수 있음. 이터러블 프로그래밍을 이용하는 이유는 무엇인가? 다시한번 상기시켜 보자면 아직 평가가 마치지 않은 상태의 Iterator를 만듦으로써 이후 filter, take 등의 함수를 통해 효율성을 최적화 할 수 있는 여지를 남겨놓기 위함이다. 지연성과 동시성을 함께 사용할 수 있는 이점을 가지기도 한다.

단, 위처럼 같은 크기의 arr 이란 배열을 별도로 만드는 것은 비효율적이므로, key-value 구성의 object에 동시성, 지연성을 적용할 수 있는 이터러블 프로그래밍 방법을 알아보자

value

먼저 value를 다루는 방법에 대해 알아보자.
위 obj1의 value를 모두 더하는 함수를 만든다고 하면 아래와 같이 구현할 수 있다.

_.go(
  obj1,
  Object.values, // 즉시 모든 값을 배열로 변환
  _.reduce((a, b) => a + b),
  console.log
); // 6

위에서는 obj1에서 value만 별도로 Object.values 메서드를 사용해 배열로 변환하는데, 즉시 모든 값을 배열로 변환한 뒤 연산을 한다는 특징을 가진다. 이를 이터러블 프로그래밍으로 바꾸면 어떨까? 아래와 같은 함수가 있다.

L.values = function* (obj){
	for (const k in obj) yield obj[k];
}
var it = L.values(obj1);
console.log([...it]); // [1, 2, 3, 4]

L.values 함수는 객체의 값을 순회하면서 iterator를 반환하여 값을 평가할 수 있게 된다. take 함수를 활용해 아래와 같이 만들 수도 있다.

var it2 = L.take(2, it); 
console.log([...it2]); // [1, 2]

필요한 값만 분리하여 계산해낼 수 있게 되는 것이다. 이를 이용해 처음 구현했던 함수를 고도화 시켜보자

_.go(
  obj1,
  L.values, // 평가를 모두 하지 않고 결과를 만들어나갈 수 있음
	L.take(2), // iterable 최소화
  _.map((a) => a + 10),
  _.reduce((a, b) => a + b),
  console.log
); // 23

위와 같이 처리하면 평가를 모두하지 않고 결과를 단계별로 만들어나갈 수 있게된다.

위와 같은 방법이 언제나 정답인 것은 아니다. 객체의 값이 obj1과 같이 4개만 있을 경우 지연평가로 하는 방식이 연산에 차이가 없거나 혹은 비용이 더 드는 일이 될 수도 있다. 하지만 객체의 길이가 길어지거나 보조함수가 하는 일이 복잡해질수록 좀 더 유리한 연산이 될 수 있는 것이다.

entries

이번에는 key, value 들이 들어있는 entries는 어떻게 지연평가하도록 만들 수 있을까

L.entries = function* (obj) {
  for (const k in obj) {
    yield [k, obj[k]];
  }
};