Метод разделения массива на пары (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]}