Comment cartographier avec index en Ruby?

438

Quelle est la façon la plus simple de convertir

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

à

[[x1, 2], [x2, 3], [x3, 4], ... , [xN, N+1]]
Misha Moroshko
la source

Réponses:

835

Si vous utilisez ruby ​​1.8.7 ou 1.9, vous pouvez utiliser le fait que les méthodes d'itérateur comme each_with_index, lorsqu'elles sont appelées sans bloc, retournent un Enumeratorobjet, que vous pouvez appeler des Enumerableméthodes comme mapon. Vous pouvez donc faire:

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

Dans 1.8.6, vous pouvez faire:

require 'enumerator'
arr.enum_for(:each_with_index).map { |x,i| [x, i+2] }
sepp2k
la source
Merci! Pourriez-vous me donner un pointeur sur la documentation pour .each_with_index.map?
Misha Moroshko
1
@Misha: mapest une méthode Enumerablecomme toujours. each_with_index, Lorsqu'il est appelé sans bloc, retourne un Enumeratorobjet (en 1.8.7+), qui se mélange dans Enumerable, de sorte que vous pouvez appeler map, select, rejectetc. sur elle comme sur un tableau, hachage, etc. gamme
sepp2k
9
IMO c'est plus simple et meilleure lecture dans 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz
4
@Phrogz: map.with_indexne fonctionne pas en 1.8.7 ( mapretourne un tableau lorsqu'il est appelé sans bloc en 1.8).
sepp2k
2
Important de noter que cela ne fonctionne pas avec .map! si vous souhaitez affecter directement le tableau sur lequel vous bouclez.
Ash Blue
259

Ruby a Enumerator # with_index (offset = 0) , donc convertissez d'abord le tableau en énumérateur en utilisant Object # to_enum ou Array # map :

[:a, :b, :c].map.with_index(2).to_a
#=> [[:a, 2], [:b, 3], [:c, 4]]
Tokland
la source
11
Je crois que c'est la meilleure réponse, car cela fonctionnera avec la carte! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Connor McKay
130

Dans ruby ​​1.9.3, il existe une méthode chaînable appelée with_index qui peut être chaînée pour mapper.

Par exemple:

array.map.with_index { |item, index| ... }
fruqi
la source
18

Sur l'obscurcissement supérieur:

arr = ('a'..'g').to_a
indexes = arr.each_index.map(&2.method(:+))
arr.zip(indexes)
Andrew Grimm
la source
12
Andrew doit avoir une grande sécurité d'emploi! :)
David J.19
9

Voici deux autres options pour 1.8.6 (ou 1.9) sans utiliser l'énumérateur:

# 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
la source
8

J'ai toujours apprécié la syntaxe de ce style:

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

L'invocation each_with_indexvous donne un énumérateur que vous pouvez facilement cartographier avec votre index disponible.

yburyug
la source
4
comment cette forme différente est-elle la réponse, donnée presque 5 ans avant la vôtre ?
Andrey Deineko
4

Une façon amusante mais inutile de le faire:

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}
Automatico
la source
Pourquoi la haine? C'est une façon fonctionnelle de le faire ET je dis même que c'est une façon stupide d'atteindre les résultats.
Automatico
l'appel à #index signifie que c'est maintenant une boucle O (N ^ 2) aussi pourquoi le +2? :)
rogerdpack
2
Au moment où j'écris A fun, but useless way. +2est de créer la sortie demandée par l'OP
Automatico
3
a = [1, 2, 3]
p [a, (2...a.size+2).to_a].transpose
Nikolay Bobrovskiy
la source
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]]
Mo Wad
la source
3
OMG! Avez-vous même lu les autres réponses? map.with_indexexiste déjà en rubis. Pourquoi suggérer de rouvrir la classe énumérable et d'ajouter quelque chose qui existe déjà?
nathanvda
Cela pourrait être un moyen plus facile d'aller pour 1.8.6 et 1.8.7 (oui certains d'entre nous l'utilisent toujours) au lieu d'avoir à utiliser des trucs plus étranges comme each_with_index.mapetc. et même ceux d'entre nous sur les versions plus récentes pourraient préférer avoir à utiliser map.with_index FWIW :)
rogerdpack
1

Je fais souvent ceci:

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

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

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

Au lieu d'itérer directement sur les éléments du tableau, vous parcourez une plage d'entiers et vous les utilisez comme indices pour récupérer les éléments du tableau.

grandinero
la source
1
Si vous lisez les autres réponses, j'espère que vous réalisez maintenant qu'il existe de meilleures approches. Vous ne savez donc pas pourquoi vous avez besoin d'ajouter cela.
nathanvda
Si la réponse d'Andrew Grimm mérite dix votes, alors celui-ci mérite au moins un!
Camille Goudeseune