두 개의 배열을 요소별로 비교해서 차이점을 찾고 싶다고 가정해 봅시다.
아니면 각 인덱스에서 가장 큰 숫자를 찾거나, 키 목록과 값 목록을 합쳐서 해시를 만들고 싶을 수도 있습니다.
이런 작업을 "어려운 방식"으로 처리하면 대략 아래 코드처럼 됩니다.
전통적인 방식의 문제점
예제:
a = [1,2,3,4,5]
b = [1,2,3,6,8]
c = a.map.with_index { |_, idx| [a[idx], b[idx]] }
# [[1, 1], [2, 2], [3, 3], [4, 6], [5, 8]]
물론 원하는 결과는 얻을 수 있지만, 코드가 그리 깔끔하지 않죠?
게다가 이 코드가 무엇을 하는지 이해하려면 잠시 멈춰서 생각해야 합니다. map.with_index 조합은 직관적이지 않기 때문입니다.
Array#zip 메서드로 더 간결하게
바로 이럴 때 Array#zip(그리고 Enumerable#zip) 메서드가 빛을 발합니다!
예제:
a = [1,2,3,4,5] b = [1,2,3,6,8] c = a.zip(b) # [[1, 1], [2, 2], [3, 3], [4, 6], [5, 8]]
어떤가요? 훨씬 낫지 않나요?
zip에 익숙하다면 코드를 보는 즉시 무슨 일이 일어나는지 이해할 수 있습니다. 익숙하지 않더라도 공식 문서(혹은 이 글!)를 잠깐 읽으면 바로 파악할 수 있을 겁니다.
zip은 두 배열의 같은 위치에 있는 요소들을 하나씩 짝지어 새로운 배열을 만들어주는 메서드입니다. 마치 옷의 지퍼(zipper)를 올리듯 양쪽 요소를 하나씩 맞춰 묶는다고 상상하면 쉽게 기억할 수 있습니다.
다른 Enumerable 메서드와 체이닝하기
zip을 사용하는 또 다른 장점은 다른 Enumerable 메서드들과 손쉽게 연결(chaining)할 수 있다는 점입니다.
예를 들어 count 메서드와 함께 사용하면:
a = [1,2,3,4,5]
b = [1,2,3,6,8]
c = a.zip(b).count { |a,b| a == b }
# 3
이 코드는 두 배열에서 값이 같으면서 위치도 동일한 요소가 정확히 몇 개인지 세어줍니다. 위 예제에서는 앞의 3개 요소가 일치하므로 3이 반환됩니다.
키와 값을 합쳐 해시 만들기
앞서 언급했듯이, zip으로 할 수 있는 또 하나의 유용한 트릭은 키 목록과 값 목록을 병합해서 해시를 만드는 것입니다.
예제:
a = %w(bacon coconut walnuts)
b = [100, 200, 300]
a.zip(b).to_h
# {"bacon"=>100, "coconut"=>200, "walnuts"=>300}
zip으로 배열을 짝지은 후 to_h를 호출하면 한 줄로 해시 변환이 끝납니다. 정말 깔끔하죠?
실전 예제: 카이사르 암호 구현하기
예제를 하나 더 보여드릴까요? 좋습니다!
여기 있습니다:
letters = Array('a'..'z')
shift = 3
translation_map = letters.zip(letters.rotate(shift)).to_h
"hello".chars.map { |ch| translation_map[ch] }.join
이것은 카이사르 암호(Caesar Cipher) 알고리즘의 멋진 구현입니다. 알파벳 배열을 3칸 회전시킨 배열과 zip으로 짝지어 치환표를 만들고, 이를 이용해 문자열의 각 글자를 암호화합니다. zip과 rotate, to_h가 조화롭게 어우러진 좋은 예시죠.
정리
이번 글에서는 두 배열을 "병합"하고 다양한 유용한 트릭에 활용할 수 있는 Array#zip 메서드에 대해 배웠습니다 🙂
- 요소별 비교가 필요할 때
map.with_index대신zip을 사용하세요 count,to_h등 다른 Enumerable 메서드와 자연스럽게 체이닝됩니다- 키-값 쌍 배열을 만들어 해시로 변환하는 데도 활용할 수 있습니다
이 글이 도움이 되었다면 즐겨 찾는 소셜 네트워크에 공유하는 것을 잊지 마세요. 더 많은 사람들이 읽을 수 있답니다!