2010-10-04 20 views
17

Các mảng kinh điển khác biệt ví dụ trong Ruby là:của Ruby mảng trừ mà không cần tháo mặt hàng nhiều hơn một lần

[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ] 

cách tốt nhất để có được những hành vi sau đây thay vì là gì?

[ 1, 1, 2, 2, 3, 3, 4, 5 ].subtract_once([ 1, 2, 4 ]) #=> [ 1, 2, 3, 3, 5 ] 

Tức là, chỉ trường hợp đầu tiên của từng mục phù hợp trong mảng thứ hai được loại bỏ khỏi mảng đầu tiên.

Trả lời

11

giá trị Subtract nhiều lần như chúng xuất hiện trong các mảng khác, hoặc bất kỳ Enumerable:

class Array 
    # Subtract each passed value once: 
    # %w(1 2 3 1).subtract_once %w(1 1 2) # => ["3"] 
    # [ 1, 1, 2, 2, 3, 3, 4, 5 ].subtract_once([ 1, 2, 4 ]) => [1, 2, 3, 3, 5] 
    # Time complexity of O(n + m) 
    def subtract_once(values) 
    counts = values.inject(Hash.new(0)) { |h, v| h[v] += 1; h } 
    reject { |e| counts[e] -= 1 unless counts[e].zero? } 
    end 

Subtract mỗi độc đáo giá trị một lần:

require 'set' 
class Array 
    # Subtract each unique value once: 
    # %w(1 2 2).subtract_once_uniq %w(1 2 2) # => [2] 
    # Time complexity of O((n + m) * log m) 
    def subtract_once_uniq(values) 
    # note that set is implemented 
    values_set = Set.new values.to_a 
    reject { |e| values_set.delete(e) if values_set.include?(e) } 
    end 
end 
+1

Tôi sẽ chấp nhận điều này nhưng sẽ tốt nếu đối số có thể chứa các giá trị trùng lặp được áp dụng lần lượt (chúng bị đè bẹp bởi chuyển đổi thành Đặt). Không chắc chắn làm thế nào bạn có thể giữ các bản sao trong khi vẫn giữ hiệu suất. (Ngoài ra tôi muốn chấp nhận một mảng và không phải là giá trị như các đối số riêng biệt, nhưng đó là một thay đổi dễ dàng) –

+0

Tôi đã cập nhật câu trả lời bằng phiên bản áp dụng nhiều lần như chúng hiện diện trong mảng khác. – glebm

+1

@glebm người đàn ông giải pháp xuất sắc! Điều này thực sự đã giúp tôi rất nhiều. Bạn đã viết này chỉ để trả lời câu hỏi SO này? Cảm ơn nhiều. –

8

Đây là tất cả tôi có thể nghĩ ra cho đến nay:

[1, 2, 4].each { |x| ary.delete_at ary.index(x) } 
+0

Điều đó có thể hơi chậm nếu 'm' (kích thước [1,2,4]) lớn – glebm

+1

Giải pháp này chỉ hoạt động nếu mọi phần tử của mảng [1,2,4] là hiện diện trong 'ary'. Nếu không thì chỉ mục của phần tử là nil. Bên trong có thể là một cái gì đó như: 'i = ary.index (x); ary.delete_at (i) nếu i' –

9
class Array 
    def subtract_once(b) 
    h = b.inject({}) {|memo, v| 
     memo[v] ||= 0; memo[v] += 1; memo 
    } 
    reject { |e| h.include?(e) && (h[e] -= 1) >= 0 } 
    end 
end 

Tôi tin rằng điều này làm những gì tôi muốn. Rất cám ơn đến @glebm

+0

Không nhìn thấy điều này - nó tốt. – glebm

+1

Gợi ý: Bên trong bơm: 'memo [v] || = 0; memo [v] + = 1; memo' Bên trong từ chối: 'h.include? (e) &&! (h [e] - = 1) .zero?' – glebm

1

Tương tự như câu trả lời @ Jeremy Ruten nhưng chiếm một thực tế rằng một số yếu tố có thể không có mặt:

# remove each element of y from x exactly once 
def array_difference(x, y) 
    ret = x.dup 
    y.each do |element| 
    if index = ret.index(element) 
     ret.delete_at(index) 
    end 
    end 
    ret 
end 

Câu trả lời này cũng sẽ không sửa đổi các mảng ban đầu vì nó hoạt động, vì vậy :

x = [1,2,3] 
y = [3,4,5] 
z = array_difference(x, y) # => [1,2] 
x == [1,2,3]    # => [1,2,3] 
Các vấn đề liên quan