¿Cómo mapear con índice en Ruby?


438

¿Cuál es la forma más fácil de convertir

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

a

[[x1, 2], [x2, 3], [x3, 4], ... , [xN, N+1]]

Respuestas:


835

Si usa Ruby 1.8.7 o 1.9, puede usar el hecho de que los métodos iteradores como each_with_index, cuando se llama sin un bloque, devuelven un Enumeratorobjeto, al que puede llamar Enumerablemétodos como mapon. Entonces puedes hacer:

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

En 1.8.6 puedes hacer:

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

¡Gracias! ¿Me podría dar un puntero a la documentación .each_with_index.map?
Misha Moroshko

1
@Misha: mapes un método de Enumerablecomo siempre. each_with_index, Cuando se le llama sin un bloque, devuelve un Enumeratorobjeto (en 1.8.7+), que mezcla en Enumerable, por lo que se puede llamar map, select, rejectetc., en que al igual que en una matriz, hachís, etc. variar
sepp2k

99
OMI, esto es más simple y mejor lectura en 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz

44
@Phrogz: map.with_indexno funciona en 1.8.7 ( mapdevuelve una matriz cuando se llama sin un bloque en 1.8).
sepp2k

2
¡Importante tener en cuenta que esto no funciona con .map! si desea afectar directamente la matriz en la que está haciendo el bucle.
Ash Blue


130

En ruby ​​1.9.3 hay un método encadenable llamado with_indexque se puede encadenar al mapa.

Por ejemplo:

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

18

Sobre la ofuscación superior:

arr = ('a'..'g').to_a
indexes = arr.each_index.map(&2.method(:+))
arr.zip(indexes)

12
¡Andrew debe tener una gran seguridad laboral! :)
David J.

9

Aquí hay dos opciones más para 1.8.6 (o 1.9) sin usar el enumerador:

# 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]]


4

Una forma divertida pero inútil de hacer esto:

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

¿Porque el odio? Es una forma funcional de hacer esto E incluso digo que es una forma tonta de lograr los resultados.
Automático

la llamada a #index significa que esto es ahora un bucle O (N ^ 2) también ¿por qué el +2? :)
rogerdpack

2
Al momento de escribir A fun, but useless way. +2es crear la salida que pide el 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
¡DIOS MIO! ¿Incluso leíste las otras respuestas? map.with_indexYa existe en rubí. ¿Por qué sugerir reabrir la clase enumerable y agregar algo que ya existe?
nathanvda

Esta podría ser una forma más fácil de usar para 1.8.6 y 1.8.7 (sí, algunos de nosotros todavía la usamos) en lugar de tener que usar cosas más extrañas como each_with_index.mapetc. e incluso aquellos de nosotros en versiones más nuevas podrían preferir tener que usar map.with_index FWIW :)
rogerdpack

1

A menudo hago esto:

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

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

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

En lugar de iterar directamente sobre los elementos de la matriz, está iterando sobre un rango de enteros y usándolos como índices para recuperar los elementos de la matriz.


1
Si lees las otras respuestas, espero que ahora te des cuenta de que hay mejores enfoques. Así que no estoy seguro de por qué necesita agregar esto.
nathanvda

Si la respuesta de Andrew Grimm merece diez votos, ¡este merece al menos uno!
Camille Goudeseune
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.