Автоматическое создание хэша

У меня есть массив ( array = ["home", "style", "honor", "home" ] и я хочу создать хэш из этого массива следующим образом:

hash = { "home" => 1, "style" => 2, "honor" => 3} #Array has two "home" element but I should give one value ("home" => 1) to both.

Но мне интересно, есть ли простой способ сделать это? Например, если я = 1 и «дом» => 1, а затем я буду 2, следуя «стилю» => 2. Должен ли я использовать циклы, enmerables? Или есть лучший способ?

«Должен ли я использовать циклы»? Ответ на этот вопрос в Ruby всегда отрицательный. Практически никогда нет причин для использования циклов.

Jörg W Mittag 26.12.2020 13:28

Какой будет результат для array = %w[home home style honor style honor home floor]?

Jörg W Mittag 26.12.2020 13:29

hash = {дом => 1, стиль => 2, честь => 3, дом => 4, пол => 5}

elixir 27.12.2020 10:46

Это невозможно. Ключи должны быть уникальными.

Jörg W Mittag 27.12.2020 10:48

Я не понимаю вашего вопроса. Желаемый результат невозможен. Неважно, что вы пытаетесь использовать для его производства. Его нельзя производить.

Jörg W Mittag 27.12.2020 12:23
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
5
148
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Первое, что нам нужно сделать, это удалить повторяющиеся элементы из Массива . Для этого мы можем использовать метод Enumerable#uniq:

array.uniq
#=> ['home', 'style', 'honor']

Далее нам нужно соединить каждый элемент с его индексом. Для этого мы можем использовать метод Enumerable#each_with_index , который дает нам перечислитель, который yield содержит двухэлементные массивы исходного элемента и его индекса:

array.uniq.each_with_index
#=> #<Enumerator: ['home', 'style', 'honor']:each_with_index>

Мы можем взглянуть на то, что происходит, когда мы используем Enumerator, преобразовав его в Array:

array.uniq.each_with_index.to_a
#=> [['home', 0], ['style', 1], ['honor', 2]]

Индексы отличаются друг от друга, но мы могли бы исправить это с помощью Enumerable#map:

array.uniq.each_with_index.map {|el, i| [el, i.succ] }
#=> [['home', 1], ['style', 2], ['honor', 3]]

В качестве альтернативы, мы могли бы исправить это позже, после того как создадим хэш с помощью метода хеш#transform_values ​​.

Другая возможность — сначала преобразовать Array в Enumerator с помощью Array#each , а затем использовать метод Enumerator#with_index, который принимает необязательный аргумент смещения:

array.uniq.each.with_index(1)
#=> #<Enumerator: #<Enumerator: ['home', 'style', 'honor']:each>:with_index(1)>

array.uniq.each.with_index(1).to_a
#=> [['home', 1], ['style', 2], ['honor', 3]]

Чтобы создать Hash, все, что нам нужно сделать, это использовать метод Enumerable#to_h для преобразования двухэлементных «пар», которые у нас уже есть, в пары ключ-значение. Enumerable#to_h также принимает необязательный блок, который является еще одним возможным способом преобразования индексов.

array.uniq.each.with_index(1).to_h
array.uniq.each_with_index.to_h {|el, i| [el, i.succ]}
array.uniq.each_with_index.to_h.transform_values(&:succ)
#=> { 'home' => 1, 'style' => 2, 'honor' => 3 }

Вот подход с одним вкладышем:

array
  .uniq
  .each_with_object({})
  .each_with_index { |(item, hash), index| 
      hash[item] = index + 1 
    }

По сути, я сначала удаляю дубликаты, затем перебираю массив (который теперь по сути является набором) с хешем и индексом под рукой и просто присваиваю следующий индекс каждому элементу. Я добавил 1 к каждому результату, чтобы он соответствовал желаемому результату.

Нужный хеш можно получить за один проход, тем самым избегая создания временных массивов.

array = ["home", "style", "honor", "home", "style", "hello"] 
e = 1.step
array.each_with_object({}) { |x,h| h[x] = e.next unless h.key?(x) }
  #=> {"home"=>1, "style"=>2, "honor"=>3, "hello"=>4}

См. форму Numeric#step, которая возвращает перечислитель. Поиск ключей (h.key?(x)) выполняется очень быстро, требуемое время почти не зависит от размера хеша.

Похоже, что h[x] ||= e.next будет столь же эффективным, поскольку у h нет default_value, а e.next никогда не будет nil. И Hash#key?, и Hash#[] используют одну и ту же функцию (hash_stlike_lookup), или мы могли бы использовать значение по умолчанию array.each_with_object(Hash.new {|h,k| h[k] = e.next}) {|x,h| h[x] }.

engineersmnky 28.12.2020 18:20

@engineersmnky, все хорошие моменты. Мне особенно нравится твой { |x,h| h[x] }, потому что он такой загадочный. Для удобочитаемости я предпочитаю h.key?(x).

Cary Swoveland 28.12.2020 20:50

если это тайна, которую вы ищете, то {_2[_1]} (ruby >= 2.7.0 Нумерованные параметры блока) или еще лучше array.each.with_index(1).with_object({}) {_2.store *_1 unless _2.key?_1[0] }

engineersmnky 28.12.2020 21:03

@engineersmnky, похоже, ты наслаждаешься своим рождественским гоголь-моголем. Обратите внимание, что еще лучше; разве _2 не увеличивается с каждым _1?

Cary Swoveland 28.12.2020 21:15

Ты прав. Для этого потребуется uniq, хотя это будет _1[1], который увеличивается с каждым _1[0] (_2 будет введенным Hash)

engineersmnky 28.12.2020 21:16

Другие вопросы по теме