Как сопоставить с индексом в Ruby?


Ответы:


835

Если вы используете ruby ​​1.8.7 или 1.9, вы можете использовать тот факт, что методы итератора, такие как each_with_index, при вызове без блока, возвращают Enumeratorобъект, который вы можете вызывать Enumerableметодами, например, mapon. Так что вы можете сделать:

arr.each_with_index.map { |x,i| [x, i+2] }

В 1.8.6 вы можете сделать:

require 'enumerator'
arr.enum_for(:each_with_index).map { |x,i| [x, i+2] }

Спасибо! Не могли бы вы дать мне указатель на документацию для .each_with_index.map?
Миша Морошко

1
@ Миша: mapэто метод, Enumerableкак всегда. each_with_index, Когда вызывается без блока, возвращает Enumeratorобъект (в 1.8.7+), который смешивает в Enumerable, так что вы можете позвонить map, select, и rejectт.д. на нем так же , как на массив, хэш, дальность и т.д.
sepp2k

9
ИМО, это проще и лучше читать в 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz

4
@Phrogz: map.with_indexне работает в 1.8.7 ( mapвозвращает массив при вызове без блока в 1.8).
sepp2k

2
Важно отметить, что это не работает с .map! если вы хотите напрямую повлиять на массив, который вы зацикливаете.
Эш Блю

259

В Ruby есть Enumerator # with_index (offset = 0) , поэтому сначала преобразуйте массив в перечислитель, используя Object # to_enum или Array # map :

[:a, :b, :c].map.with_index(2).to_a
#=> [[:a, 2], [:b, 3], [:c, 4]]

11
Я считаю, что это лучший ответ, потому что он будет работать с картой! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Коннор Маккей

130

В ruby ​​1.9.3 есть цепной метод, with_indexкоторый можно связать для отображения.

Например:

array.map.with_index { |item, index| ... }


9

Вот еще две опции для 1.8.6 (или 1.9) без использования перечислителя:

# Fun with functional
arr = ('a'..'g').to_a
arr.zip( (2..(arr.length+2)).to_a )
#=> [["a", 2], ["b", 3], ["c", 4], ["d", 5], ["e", 6], ["f", 7], ["g", 8]]

# The simplest
n = 1
arr.map{ |c| [c, n+=1 ] }
#=> [["a", 2], ["b", 3], ["c", 4], ["d", 5], ["e", 6], ["f", 7], ["g", 8]]

8

Мне всегда нравился синтаксис этого стиля:

a = [1, 2, 3, 4]
a.each_with_index.map { |el, index| el + index }
# => [1, 3, 5, 7]

Invoking each_with_indexдает вам перечислитель, который вы можете легко отобразить с помощью доступного индекса.



4

Забавный, но бесполезный способ сделать это:

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}

Почему ненавижу? Это функциональный способ сделать это, и я даже говорю, что это глупый способ достичь результатов.
Автомат

вызов #index означает, что теперь это цикл O (N ^ 2) и почему +2? :)
rogerdpack

2
Как я пишу A fun, but useless way. +2создать выход, который запрашивает OP
Automatico


1
module Enumerable
  def map_with_index(&block)
    i = 0
    self.map { |val|
      val = block.call(val, i)
      i += 1
      val
    }
  end
end

["foo", "bar"].map_with_index {|item, index| [item, index] } => [["foo", 0], ["bar", 1]]

3
О, МОЙ БОГ! Вы даже читали другие ответы? map.with_indexуже существует в ruby. Зачем предлагать заново открыть перечислимый класс и добавить что-то, что уже существует?
Натанвда

Это может быть более простой способ перейти на 1.8.6 и 1.8.7 (да, некоторые из нас все еще используют его) вместо того, чтобы использовать более странные вещи, такие как each_with_index.mapи т. Д., И даже те из нас, кто работает в более новых версиях, могут предпочесть использовать map.with_index FWIW :)
rogerdpack

1

Я часто делаю это:

arr = ["a", "b", "c"]

(0...arr.length).map do |int|
  [arr[int], int + 2]
end

#=> [["a", 2], ["b", 3], ["c", 4]]

Вместо прямой итерации по элементам массива вы выполняете итерацию по целому ряду целых чисел и используете их в качестве индексов для извлечения элементов массива.


1
Если вы прочитаете другие ответы, я надеюсь, что вы теперь понимаете, что есть лучшие подходы. Так что не уверен, почему вам нужно было добавить это.
Натанвда

Если ответ Эндрю Гримма заслуживает десяти голосов, то этот заслуживает хотя бы одного!
Камиль Гудесюн
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.