2009-09-17 3 views
0

У меня есть массив в Ruby. Я хочу:Массирование массива Ruby на основе свойств индекса

  1. Получите подмножество элементов на основе их положения в массиве - скажем каждый пятый элемент. Я могу сделать это с помощью each_index, или расширить и создать метод select_with_index.

  2. Выполнение некоторых операций над подмножеством, которое зависит от всего подмножества - скажем, subset.map {| element | subset.sum - элемент}

  3. Это немного, я застрял на: Создать новый массив с правильными элементами заменены элементами на шаге 2. Например:

Так мои весьма запутанное пример может иметь:

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] 

Как я могу выполнить замену элегантным способом? Есть ли способ создать метод, который будет сочетать два массива и взять блок {| i | i% 5 == 0}?

(Это мотивировано подходом к написанию компактный Sudoku Solver, чтобы узнать немного больше рубин ...)

EDIT: Были изменены примеры значений. Надеюсь, теперь это понятно.

+0

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

+0

D'oh. Нет оправдания. –

ответ

0

Вы можете сделать это за один проход, если вам не нужно знать длину подмножества.

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

Предполагая, что у вас есть Array#sum реализованы в другом месте, вы можете сделать это в два прохода, как так:

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

Мой пример может быть запутанным, поскольку элементы соответствуют индексу. Позвольте мне изменить его, и это может быть яснее. Извиняюсь. –

+0

'sum' почти встроен, если вы используете аккумулятор:' arr.inject (&: +) ' – samuil

1

Предполагая, метод суммы является один из Rails, это может работать:

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?} 

Я оставляю это для вас, чтобы реорганизовать его во что-то полезное :) В принципе, теория состоит в том, чтобы сохранить все индексы в исходном массиве даже в подмножестве. Таким образом, легко узнать, какие из них заменить позже. Вы также можете использовать хэш для него, если есть более сложные вычисления.

Вот немного более компактная версия этого:

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

Да, похоже, что это возможно. Метод суммы был только что составлен, мог бы использовать что-то вроде инъекции (0) {| s, i | s + = i, если i.nil?} ... –

+0

Я не думаю, что инъекция понравится, чтобы вы вернули нуль. инъекционные (0) {| м, я | i.nil? ? m: m + i} было бы лучше :) И, да. Это в основном способ, которым Rails это делает, за исключением проверки nil. –

+0

Согласен - не проверял код. –

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

Я бы, наверное, просто решить эту проблему с 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] 

Или

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] 
Смежные вопросы