본문 바로가기

TIL

_.flatten() ft. debugger | Lodash [자바스크립트]

Lodash의 _.flatten() 써보기. 

 

우선 _.flatten을 만들면서 재사용한 _.each()부터.

_.each = function (collection, iterator) {
  if (Array.isArray(collection)) {
    for (let i = 0; i < collection.length; i++) {
      iterator(collection[i], i, collection);
    }
  } else if (collection && typeof collection === 'object') {
    for (const key in collection) {
      iterator(collection[key], key, collection);
    }
  }
}

_.some()도 사용했다.

_.some = function(collection, iterator) {
  iterator = iterator || _.identity;
  return !_.every(collection, item => !iterator(item));
}

_.some은 _.every()를 사용했네.

_.every = function(collection, iterator) {
  iterator = iterator || _.identity;

  return _.reduce(
    collection,
    (accumulator, item) => iterator(item) ? accumulator : false,
    true
  )

이건 또 _.reduce()를 사용했다. 귀찮지만 복습한다 생각하고 계속 거슬러 올라가자.

_.reduce = function(collection, iterator, accumulator) {
  if (accumulator !== undefined) {
    _.each(collection, function(item) {
      accumulator = iterator(accumulator, item)
    })
  } else if (accumulator === undefined) {
    accumulator = collection[0];

    for (let i = 1; i < collection.length; i++) {
      accumulator = iterator(accumulator, collection[i]);
    }
  }

  return accumulator;
}

 

그럼 본격적으로 _flatten(). 배열 속에 배열이 있으면 요소들을 꺼내서 가장 바깥쪽 배열로 옮겨주는 함수. 즉 배열의 깊이?를 없애는 함수. 배열 속 배열 속에 배열 속 배열 속 배열 속 배열 속 배열이 계속 있을 수 있다.

 

_.reduce()를 재사용해서 짜보려다가 실패만 무수하게 겪었다. 그러다가 새로 배운 재귀함수를 써보기로. 처음이라 좀 힘겨웠다.

 

고난 끝에 처음으로 성공한 코드는

_.flatten = function(nestedArray, result) {
  result = [];

  if (!_.some(nestedArray, Array.isArray)) {
    result = result.concat(nestedArray);
  } else {
    for (let i = 0; i < nestedArray.length; i++) {
      let element = nestedArray[i];

      if (!Array.isArray(element)) result.push(element);
      else result = result.concat(_.flatten(element));
    }
  }

  return result;
};

주어진 함수 속에 배열이 또 있는지 먼저 확인한다. 없으면 result에 해당 배열을 덧붙이고 result로 다시 할당한다.

배열이 안에 또 있으면 배열을 쭉 훑으면서 배열이 아닌 요소는 result에 밀어넣고 배열인 요소는 _.flatten() 함수에 다시 집어넣어서 돌린다. 평탄화가 끝날 때까지 계속 돌다가 배열 속 배열이 다 없어지면 else가 성립하지 않아 재귀가 끝나고 result를 반환한다.

 

두 번째 방식은

_.flatten = function(nestedArray, result) {
  result = [];

  _.each(nestedArray, function(element) {
    if (!Array.isArray(element)) result.push(element);
    else if (Array.isArray(element)) {
      result = result.concat(_.flatten(element));
    }
  })

  return result;
};

비슷하지만 처음에 배열 속에 배열이 있는지 확인하는 과정을 안 거친다. 조금 더 효율적이길..

처음부터 훑으면서 배열이 아닌 요소는 result에 밀어넣고 배열인 요소는 _flatten 속으로 다시 들어간다.

 

함수를 썼다는 것 자체보다도 debugger를 사용해서 문제를 해결했다는 점이 뿌듯했다. 처음으로 써봤는데 함수가 돌아가는 과정을 들여다볼 수 있어서, 문제를 눈으로 확인할 수 있어서 코드를 고치는 게 한층 수월했다. 

이를테면 중간에 result = [a]인 상황에서 다음 _.flatten이 돌아갔을 때 [b]가 붙어서 result = [a, b]로 나와야 하는데 result = [a, a, b]로 나오는 문제가 발생했다. 기존 result에 단순히 붙는 게 아니라 붙은 결과물이 기존 result에 붙는 것이었다.

그런데 debugger를 코드에 넣어서 과정을 살펴보니 문제가 발생하는 지점을 알 수 있어서 고칠 수 있었다. 처음에

result = result || [];

라고 쓴 게 문제였다.

 

어쨌든 이젠 처음에 시도했던 대로 _.reduce를 사용해서 _.flatten을 써보려 한다.