Метод разделения массива на пары (element => остальные элементы)
Учитывая литерал массива, я хотел бы создать хэш, где ключи являются элементами из массива, а значения - это массивы, содержащие остальные/оставшиеся элементы.
Input:
[1, 2, 3]
Вывод:
{1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
Легко ввести переменную:
arr = [1, 2, 3]
arr.map { |i| [i, arr - [i]] }.to_h
Но с литералом массива единственное решение, которое я мог бы найти, включает instance_exec
или instance_eval
, что кажется хакерским:
[1, 2, 3].instance_exec { map { |i| [i, self - [i]] } }.to_h
Могу ли я игнорировать встроенный метод или очевидное решение? group_by
, combination
, permutation
и partition
, похоже, не помогают.
Ответы
Ответ 1
Я придумал что-то вроде этого:
[1,2,3].permutation.to_a.map{ |e| [e.shift, e] }.to_h
Однако у этого есть недостаток: он много раз назначает один и тот же ключ, но так как вы не заботитесь о последовательности элементов внутри этого, это может быть "достаточно хорошим" решением.
Ответ 2
У меня появилась другая идея. Вот что:
a = [1, 2, 3]
a.combination(2).with_object({}) { |ar, h| h[(a - ar).first] = ar }
# => {3=>[1, 2], 2=>[1, 3], 1=>[2, 3]}
Измененная версия Piotr Kruczek.
[1,2,3].permutation.with_object({}) { |(k, *v), h| h[k] = v }
# => {1=>[3, 2], 2=>[3, 1], 3=>[2, 1]}
Ответ 3
Я бы пошел с решением Piotr, но для удовольствия, у меня другой подход:
[1,2,3].inject([[],{}]) do |h_a, i|
h_a[0] << i
h_a[1].default_proc = ->(h,k){ h_a[0] - [k]}
h_a
end.last
Это гораздо больше из-за взлома и менее элегантного.
Ответ 4
Вот три способа использования Object # tap. Есть ли аргумент, запрещающий его использование?
Все три метода работают, если массив содержит дубликаты; например:
[1,2,2]....
#=> {1=>[1, 2], 2=>[1, 1]}
# 1
[1,2,2].tap do |a|
a.replace(a.cycle.each_cons(a.size).first(a.size).map { |k,*v| [k,v] })
end.to_h
#=> {1=>[2, 3], 2=>[3, 1], 3=>[1, 2]}
# 2
[1,2,3].tap do |a|
@h = a.map do |i|
b = a.dup
j = b.index(i)
b.delete_at(j)
[i,b]
end.to_h
end
@h #=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
# 3
[1,2,3].map.with_index { |*e| e.reverse }.to_h.tap do |h|
a = h.values
h.replace(a.each_with_object({}) do |e,g|
b = a.dup
i = b.index(e)
b.delete_at(i)
g.update(e=>b)
end)
end
#=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
Добавление
Код в последних двух методах может быть упрощен с использованием столь необходимого метода Array#difference
, как это определено в моем ответе здесь. # 3, например, становится:
[1,2,3].map.with_index { |*e| e.reverse }.to_h.tap do |h|
a = h.values
h.replace(a.each_with_object({}) { |e,g| g.update(e=>a.difference([e])) })
end
#=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
Ответ 5
[1,2,3,4].each_with_object({}) do |n,h|
h.each_key { |k| h[k] << n }
h[n] = h.keys
end
#=> {1=>[2, 3, 4], 2=>[1, 3, 4], 3=>[1, 2, 4], 4=>[1, 2, 3]}