Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

루비 해시(Ruby Hash) 완벽 가이드 – 개념, 예제, 핵심 메서드 총정리

루비 해시(Ruby Hash)란 무엇일까요?

해시는 데이터를 고유한 키(key)-값(value) 쌍의 형태로 저장하는 자료구조입니다. 배열과 달리 숫자 인덱스가 없으며, 대신 키를 사용하여 해시의 값에 접근합니다.

해시가 활용되는 대표적인 예

  • 국가 이름과 해당 국가 코드 목록 (예: ES ⇾ 스페인)
  • 사전 — 각 단어마다 여러 개의 정의가 연결되어 있는 형태
  • 도메인 이름과 그에 대응하는 IP 주소

적절한 상황에서 해시를 활용하면 루비 프로그램의 실행 속도를 크게 높일 수 있습니다.

다시 말해:

데이터가 사전과 같은 형태이거나, 키와 연관된 값으로 그룹화할 수 있는 구조라면 해시가 최적의 선택입니다.

해시 생성 방법

해시가 무엇인지는 이제 감을 잡았으니, 실제로 어떻게 만드는지 알아보겠습니다.

다음과 같이 생성합니다:

{}

위 코드는 빈 해시입니다.

키-값 쌍 세 개를 가진 해시는 다음과 같습니다:

{ a: 1, b: 2, c: 3 }

여기서 a는 키이고, 1은 그 키에 대응하는 값입니다. 각 키-값 쌍은 쉼표로 구분됩니다.

자, 이제 루비 프로젝트에서 해시를 실제로 활용하는 방법과 자주 사용되는 해시 메서드들을 살펴보겠습니다.

루비 해시에 값 저장하기

앞서 본 것처럼 초기값을 설정한 상태로 해시를 만들 수 있습니다.

또 다른 예제입니다:

fruits = { coconut: 1, apple: 2, banana: 3 }

기존 해시에 새로운 값을 추가할 수도 있습니다.

다음과 같이 말이죠:

fruits[:orange] = 4

여기서 :orange는 해시 키이고, 4는 그에 대응하는 값입니다.

값을 조회할 때는 콜론이 단어 앞에 붙고(:orange), 해시를 생성할 때는 단어 뒤에 붙는(orange:) 이유가 궁금하신가요? 이는 해시 로켓(=>) 기호 없이 해시를 더 깔끔하게 작성할 수 있게 해주는 문법입니다. 물론 => 방식도 유효하지만, 비교적 오래된 방식입니다.

해시의 값으로는 모든 루비 객체를 사용할 수 있습니다.

예를 들면:

  • 문자열(String)
  • 정수(Integer) 및 부동소수점(Float)
  • 배열(Array)

키 역시 어떤 객체든 가능하지만, 실무에서는 심볼(:banana 같은 형태)과 문자열이 가장 흔하게 사용됩니다.

기억하세요…

키는 고유합니다. 즉, :orange 키나 :apple 키는 각각 하나만 존재할 수 있습니다.

같은 키를 두 번 추가하면 기존 값이 새로운 값으로 덮어써집니다.

해시에서 값 가져오기

해시의 값은 반드시 키를 통해 접근합니다.

만약 값을 직접 순회하며 접근해야 하는 상황이라면, 해시가 데이터 구조로 적합하지 않을 수 있습니다.

예제:

fruits[:orange]
# 4

이것이 바로 해시의 핵심입니다. 키를 통해 빠르게 항목을 조회하는 것이죠.

존재하지 않는 키에 접근하면 nil이 반환됩니다.

fruits[:peach]
# nil

대안으로 fetch 메서드를 사용하면 기본값(default value)을 지정할 수 있습니다.

예제:

fruits.fetch(:peach, 0)

만약 두 번째 인자인 기본값 없이 fetch를 호출하면, 루비는 KeyError 예외를 발생시킵니다.

이는 오히려 유용한데, 어떤 키가 누락되었는지 명확히 알 수 있기 때문입니다.

두 개의 루비 해시 병합하기

두 개의 해시를 하나의 새로운 해시로 합칠 수 있습니다.

어떤 메서드를 사용할까요?

이름에서 바로 알 수 있듯이, merge 메서드를 사용합니다.

사용 방법은 다음과 같습니다:

defaults    = { a: 1, b: 2, c: 3 }
preferences = { c: 4 }

defaults.merge!(preferences)
# {:a=>1, :b=>2, :c=>4}

키는 고유하기 때문에, 나중에 들어온 값이 기존 값을 덮어쓴다는 점에 주목하세요.

이 특성을 활용하면 재미있는 해결책을 만들 수 있습니다. 예를 들어 "기본 설정(defaults)" 해시를 만들어 두고, 사용자가 자신만의 해시를 전달하여 기본값을 덮어쓰도록 하는 방식이 있습니다.

키 병합 과정을 더 세밀하게 제어하고 싶다면 블록(block)을 전달할 수도 있습니다.

다음과 같이 말이죠:

defaults.merge!(preferences) { |key, old, new| [old, new].max }

여기서 olddefaults에서 넘어온 값이고, newpreferences에서 넘어온 값입니다.

하나의 키에 여러 값 담기

사전을 생각해 보세요.

단어는 고유하지만, 하나의 단어에 여러 개의 값(정의)이 연결될 수 있습니다.

루비에서도 이것이 가능합니다!

예제:

dictionary = {
  opportunity: [
    "a set of circumstances that makes it possible to do something",
    "a situation or condition favorable for attainment of a goal"
  ],
  creativity: [
    "the use of imagination or original ideas to create something new",
    "the ability to create",
    "the process where new ideas emerge from combining existing ideas in new ways"
  ]
}

dictionary[:creativity][1]

dictionary[:creativity]는 배열을 반환하고, 여기에 [1]을 붙이면 그 배열의 두 번째 요소에 접근하게 됩니다.

즉:

키는 심볼이고, 값은 배열입니다. 해시에 접근하면 배열이 반환되며, 이후에는 일반적인 배열처럼 다루면 됩니다.

해시 정렬하기

배열을 정렬할 수 있다는 것은 잘 알고 있겠지만, 해시도 정렬할 수 있다는 사실을 알고 계셨나요?

해시에 sort를 호출하면 키를 기준으로 정렬됩니다.

예제:

{ b: 1, a: 2 }.sort

# [[:a, 2], [:b, 1]]

물론 값 기준으로도 정렬할 수 있습니다:

{ c: 3, b: 1, a: 2 }.sort_by(&:last)

눈치채셨겠지만, 해시를 정렬한 결과는 또 다른 해시가 아닙니다.

배열입니다!

하지만 to_h 메서드를 사용하면 이 배열을 다시 해시로 변환할 수 있습니다.

해시의 모든 키와 값 가져오기

해시의 모든 키 목록이 필요하신가요? 좋은 소식이 있습니다. 바로 그런 용도의 메서드가 존재합니다!

바로 이것입니다:

{ apple: 1, banana: 2 }.keys

# [:apple, :banana]

값들만 담은 배열을 반환하는 메서드도 있습니다:

{ apple: 1, banana: 2 }.values

# [1, 2]

키 목록 전체가 아니라 특정 키의 존재 여부만 확인하고 싶다면 key? 메서드를 사용하세요.

이 메서드는 true 또는 false를 반환합니다.

마무리

지금까지 키-값 쌍으로 구성된 유용한 자료구조인 루비 해시에 대해 배웠습니다. 키를 통해 해시에 접근하는 방법과, 해시에 새로운 데이터를 저장하는 방법도 익혔습니다.

이제 irb(또는 pry)를 열고 직접 해시를 가지고 놀아보세요!

읽어주셔서 감사합니다 🙂