2009-09-17 9 views
0

J'ai un tableau de Ruby. Je veux:manipulation des tableaux Ruby en fonction des propriétés de l'indice

  1. Obtenez un sous-ensemble des éléments en fonction de leur position dans le tableau - disons tous les 5 éléments. Je peux le faire avec each_index, ou étendre et créer une méthode select_with_index.

  2. Effectuez une opération sur le sous-ensemble que dépend de l'ensemble du sous-ensemble - disons subset.map {| element | subset.sum - élément}

  3. C'est le bit que je suis bloqué sur: Créer un nouveau tableau avec les éléments corrects remplacés par les éléments à l'étape 2. Par exemple:

donc ma très alambiquée exemple pourrait avoir:

Start: [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564] 

Select: [3,2,122] 

Map:  [124,125,5] 

Replace: [124,0,6,11,77,125,1,5,48,9,5,0,43,13,564] 

Comment puis-je effectuer le remplacement d'une manière élégante? Y at-il un moyen de créer une méthode qui prendrait combiner les deux tableaux et prendre un bloc {| i | i% 5 == 0}?

(Ceci est motivé par une approche à la rédaction d'un solveur Sudoku compact afin d'apprendre un peu plus Ruby ...)

EDIT: ont changé les valeurs par exemple. J'espère que c'est plus clair maintenant.

+0

Um, (3 + 2 + 122) -122! = 6 –

+0

D'oh. Pas d'excuses. –

Répondre

0

Vous pouvez le faire en une seule passe si vous n'avez pas de connaître la longueur du sous-ensemble.

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] 
p a.map {|i| i % 5 == 0 ? "foo" : i } 
# => ["foo", 1, 2, 3, 4, "foo", 6, 7, 8, 9, "foo", 11, 12, 13, 14, "foo"] 

Si l'on suppose que vous avez un Array#sum mis en œuvre ailleurs, vous pouvez le faire en deux passes comme ceci:

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] 
sum = a.select {|i| i % 5 == 0 }.sum 
p a.map {|i| i % 5 == 0 ? sum - i : i } 
# => [30, 1, 2, 3, 4, 25, 6, 7, 8, 9, 20, 11, 12, 13, 14, 15] 
+0

Mon exemple peut prêter à confusion lorsque les éléments correspondent à l'index. Laissez-moi le changer et cela pourrait être plus clair. Excuses. –

+0

'sum' est presque intégré si vous utilisez l'accumulateur:' arr.inject (&: +) ' – samuil

1

En supposant que la méthode de la somme est celle de Rails, cela pourrait fonctionner:

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] 
b = [] 
a.each_index {|i| b[i] = a[i] if i%5 == 0} 
c = b.map{|p| p.nil? ? nil : b.sum{|i| i.nil? ? 0 : i} - p} 
c.each_index {|i| a[i] = c[i] unless c[i].nil?} 

Je laisse à vous de ce factoriser dans quelque chose d'utile :) Fondamentalement, la théorie est de garder tous les index du tableau d'origine, même dans le sous-ensemble. De cette façon, il est facile de savoir lesquels remplacer plus tard. Vous pouvez également utiliser une table de hachage pour s'il y a des calculs plus complexes.

Voici un peu plus version compacte de celui-ci:

a = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564] 
b = [] 

a.each_index {|i| b[i] = a[i] if i%5 == 0} 
b.each_with_index {|obj, i| 
    a[i] = b.inject(0){|m,v| v.nil? ? m : v} - obj unless obj.nil?} 
+0

Oui, qui ressemble le pourrait. La méthode sum était juste composée, pourrait utiliser quelque chose comme injecter (0) {| s, i | s + = i sauf si i.nil?} ... –

+0

Je ne pense pas injecter aimerait que vous reveniez à zéro. injecter (0) {| m, i | i.nil? ? m: m + i} serait mieux :) Et, ouais. C'est à peu près la façon dont Rails le fait, sauf pour le contrôle nul. –

+0

D'accord - n'avait pas testé le code. –

2
a = [3, 0, 6, 11, 77, 2, 1, 5, 48, 9, 122, 0, 43, 13, 564] 

# per your requirements 
def replace_indices(ary, &index_selector) 
    indices = ary.each_index.select(&index_selector) 
    sum = indices.inject(0) {|sum, i| sum += ary[i]} 
    indices.each {|i| ary[i] = sum - ary[i]} 
    ary 
end 

p new = replace_indices(a.dup) {|i| i % 5 == 0} 

# just pass the "index step value" 
def replace_each_step(ary, step) 
    sum = 0 
    ary.each_index . 
     select {|i| i % step == 0} . 
     collect {|i| sum += ary[i]; ary[i]} . 
     each_with_index {|e,i| ary[i*step] = sum - e} 
    ary 
end 

p new = replace_each_step(a.dup, 5) 
1

je serais probablement résoudre tout cela avec Enumerable#enum_for(:each_with_index)

require 'enumerator' 

values = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564] 

subset_with_indexes = values.enum_for(:each_with_index).select { |v,i| i % 5 == 0 } 
#=> [ [3,0], [2,5], [122,10] ] 

subset_sum = subset_with_indexes.inject(0) { |s,(v,i)| s+v } 
#=> 127 

subset_with_indexes.each do |v,i| 
    values[i] = subset_sum - v 
end 

values #=> [124, 0, 6, 11, 77, 125, 1, 5, 48, 9, 5, 0, 43, 13, 564] 

Ou

require 'enumerator' 

values = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564] 
values_with_indexes = values.enum_for(:each_with_index) 

subset_sum = values_with_indexes.inject do |s,(v,i)| 
    i % 5 == 0 ? s + v : s 
end #=> 127 

new_values = values_with_indexes.map do |v,i| 
    i % 5 == 0 ? subset_sum - v : v 
end #=> [124, 0, 6, 11, 77, 125, 1, 5, 48, 9, 5, 0, 43, 13, 564] 
Questions connexes