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

438

Какой самый простой способ конвертировать

[x1, x2, x3, ... , xN]

в

[[x1, 2], [x2, 3], [x3, 4], ... , [xN, N+1]]
Миша Морошко
источник

Ответы:

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] }
sepp2k
источник
Спасибо! Не могли бы вы дать мне указатель на документацию для .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]]
tokland
источник
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| ... }
fruqi
источник
18

Сверху запутывание:

arr = ('a'..'g').to_a
indexes = arr.each_index.map(&2.method(:+))
arr.zip(indexes)
Эндрю Гримм
источник
12
У Андрея должна быть отличная работа! :)
Дэвид Дж
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]]
Phrogz
источник
8

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

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

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

yburyug
источник
4
как эта другая форма ответ, данный почти за 5 лет до твоего ?
Андрей Дейнеко
4

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

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}
Automatico
источник
Почему ненавижу? Это функциональный способ сделать это, и я даже говорю, что это глупый способ достичь результатов.
Автомат
вызов #index означает, что теперь это цикл O (N ^ 2) и почему +2? :)
rogerdpack
2
Как я пишу A fun, but useless way. +2создать выход, который запрашивает OP
Automatico
3
a = [1, 2, 3]
p [a, (2...a.size+2).to_a].transpose
Николай Бобровский
источник
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]]

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

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