2015-05-08 27 views
12

Cho mảng chữ, tôi muốn tạo một băm trong đó các khóa là các phần tử của mảng và giá trị là mảng chứa các phần tử còn lại .Phương pháp tách mảng thành các phần tử (phần tử => phần tử còn lại)

Input:

[1, 2, 3] 

Output:

{1=>[2, 3], 2=>[1, 3], 3=>[1, 2]} 

Thật dễ dàng nếu tôi giới thiệu một biến:

arr = [1, 2, 3] 
arr.map { |i| [i, arr - [i]] }.to_h 

Nhưng với một mảng đen, giải pháp duy nhất tôi có thể đưa ra có liên quan đến instance_exec hoặc instance_eval, có vẻ như bị hack:

[1, 2, 3].instance_exec { map { |i| [i, self - [i]] } }.to_h 

Tôi có xem xét phương pháp tích hợp hoặc giải pháp hiển nhiên không? group_by, combination, permutationpartition dường như không giúp ích gì.

+0

không liên quan, nhưng vì bạn không vượt qua bất kỳ đối số nào để chặn, tại sao không 'instance_eval'? –

+0

@AndreyDeineko có sự khác biệt nào không? – Stefan

+1

'[[1,2,3]] * 2' ⇒ bây giờ bạn có một bản sao :) – mudasobwa

Trả lời

6

tôi đã đi lên với một cái gì đó như thế này:

[1,2,3].permutation.to_a.map{ |e| [e.shift, e] }.to_h 

Tuy nhiên điều này có một lỗ hổng: nó gán cùng một chìa khóa nhiều lần, nhưng kể từ khi bạn không quan tâm đến trình tự của các yếu tố bên trong này có thể là một giải pháp "đủ tốt".

+1

Đó là một giải pháp thú vị. –

+0

Thật vậy, đó là một cách tiếp cận sáng tạo :-) – Stefan

+2

Bạn có thể bỏ qua 'to_a' và chia mảng với *:' [1,2,3] .permutation.map {| k, * vs | [k, vs]} .to_h' – Stefan

1

Tôi muốn đi với giải pháp Piotr, nhưng đối với những niềm vui của nó, tôi có một cách tiếp cận khác nhau:

[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 

Đó là nhiều hơn nữa của một hack và ít thanh lịch, mặc dù.

+0

Mặc dù proc mặc định trả về các giá trị chính xác, nhưng tôi không thể liệt kê 'keys' hoặc lặp lại hash bằng cách sử dụng' each'. – Stefan

2

Tôi có một ý tưởng khác. Đây là:

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

Phiên bản sửa đổi Piotr Kruczek.

[1,2,3].permutation.with_object({}) { |(k, *v), h| h[k] = v } 
# => {1=>[3, 2], 2=>[3, 1], 3=>[2, 1]} 
+2

Kết hợp là những gì tôi nghĩ lúc đầu, nhưng vấn đề ở đây là như một tham số bạn phải vượt qua 'a.length-1' và toàn bộ điểm không được gán mảng cho một biến:/ –

+0

@PiotrKruczek Ok .. Vì vậy, nếu tôi gán nó cho một biến _life là easy_ .. :) –

1

Dưới đây là ba cách để sử dụng Object#tap. Có một lập luận cấm sử dụng nó không?

Tất cả ba phương pháp đều hoạt động nếu mảng chứa các ký tự dups; ví dụ:

[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]} 

Phụ Lục

Mã trong hai phương pháp sau có thể được đơn giản hóa bằng cách sử dụng các phương pháp rất cần thiết Array#difference, như đã định nghĩa nó trong câu trả lời của tôi here. # 3, ví dụ: trở thành:

[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]} 
+0

'tap' chắc chắn là tốt hơn' instance_exec'/'instance_eval';) – Stefan

Các vấn đề liên quan