2012-10-03 28 views
15

Thậm chí đến từ javascript điều này có vẻ tồi tệ với tôi:Đặt lại vị trí một yếu tố vào phía trước của một mảng trong Ruby

irb 
>> a = ['a', 'b', 'c'] 
=> ["a", "b", "c"] 
>> a.unshift(a.delete('c')) 
=> ["c", "a", "b"] 

Có cách nào dễ đọc hơn đặt một yếu tố vào phía trước của một mảng?

Sửa mã thực tế của tôi:

if @admin_users.include?(current_user) 
    @admin_users.unshift(@admin_users.delete(current_user)) 
end 
+0

câu hỏi không rõ ràng, bạn yêu cầu một cách dễ đọc hơn "đặt một phần tử vào trước một mảng" ('Chèn # mảng (chỉ mục, giá trị)'?) nhưng ví dụ sử dụng 'xóa' và có vẻ như bạn muốn Vòng xoay. – tokland

+0

Tôi đã lặp lại câu hỏi. – Duopixel

+0

ok, giờ đã rõ. Mặc dù nó có được cập nhật tại chỗ không? tại sao không trả về một mảng mới? – tokland

Trả lời

5

Đây là một vấn đề phức tạp hơn so với nó có vẻ. Tôi định nghĩa các xét nghiệm sau:

describe Array do 
    describe '.promote' do 
    subject(:array) { [1, 2, 3] } 

    it { expect(array.promote(2)).to eq [2, 1, 3] } 
    it { expect(array.promote(3)).to eq [3, 1, 2] } 
    it { expect(array.promote(4)).to eq [1, 2, 3] } 
    it { expect((array + array).promote(2)).to eq [2, 1, 3, 1, 2, 3] } 
    end 
end 

sort_by bởi @Duopixel đề xuất là thanh lịch nhưng sản xuất [3, 2, 1] cho kỳ thi thứ hai.

class Array 
    def promote(promoted_element) 
    sort_by { |element| element == promoted_element ? 0 : 1 } 
    end 
end 

@tadman sử dụng delete, nhưng điều này sẽ xoá tất cả các yếu tố phù hợp, vì vậy đầu ra của thử nghiệm thứ tư là [2, 1, 3, 1, 3].

class Array 
    def promote(promoted_element) 
    if (found = delete(promoted_element)) 
     unshift(found) 
    end 

    self 
    end 
end 

tôi đã cố gắng sử dụng:

class Array 
    def promote(promoted_element) 
    return self unless (found = delete_at(find_index(promoted_element))) 
    unshift(found) 
    end 
end 

Nhưng điều đó đã không kiểm tra thứ ba vì delete_at không thể xử lý nil. Cuối cùng, tôi quyết định chọn:

class Array 
    def promote(promoted_element) 
    return self unless (found_index = find_index(promoted_element)) 
    unshift(delete_at(found_index)) 
    end 
end 

Ai biết một ý tưởng đơn giản như promote có thể là rất khó khăn?

+0

Đã lâu rồi kể từ câu hỏi này nhưng điều này có vẻ tốt! – Duopixel

+0

Cảm ơn! Tôi không thể tin được cái lỗ hổng như thế nào ở phía dưới lỗ hổng như vậy. – dankohn

+0

Điều này là tốt. Chỉ cần một quan sát: lựa chọn của bạn 'thúc đẩy' presupposes rằng nó chỉ là nghĩa vụ phải di chuyển sự xuất hiện đầu tiên của 'promotion_element'. 'Array # delete' loại bỏ tất cả các phần tử khớp với tham số. Nó sẽ là không hợp lý để mong đợi hành vi trực giao từ'promote' và kết quả của bài kiểm tra thứ tư của bạn sẽ là '[2, 2, 1, 3, 1, 3]'? Để sử dụng 'quảng cáo' của bạn như một thành phần trong một phương thức di chuyển tất cả các lần xuất hiện của' promotion_element' sẽ yêu cầu lặp lại mảng và so sánh (tôi nghĩ ...). – Huliax

14

lẽ Array#rotate sẽ làm việc cho bạn:

['a', 'b', 'c'].rotate(-1) 
#=> ["c", "a", "b"] 
+1

Điều cần biết. Thật không may nó không phải là vấn đề đặt phần tử cuối cùng vào mặt trước, nó lấy một phần tử tùy ý và đặt nó lên phía trước. Tôi đã cập nhật câu hỏi và lời xin lỗi của mình. – Duopixel

+0

Tôi không biết phương pháp đó. – Papipo

4

Nếu bằng cách "thanh lịch" bạn có nghĩa là dễ đọc hơn thậm chí phải trả giá bằng việc phi tiêu chuẩn , bạn luôn có thể viết phương pháp của riêng mình để tăng cường Array:

class Array 
    def promote(value) 
    if (found = delete(value)) 
     unshift(found) 
    end 

    self 
    end 
end 

a = %w[ a b c ] 
a.promote('c') 
# => ["c", "a", "b"] 
a.promote('x') 
# => ["c", "a", "b"] 

Hãy nhớ rằng điều này sẽ chỉ định vị lại một trường hợp duy nhất của một giá trị. Nếu có một vài trong mảng, những cái tiếp theo có lẽ sẽ không được di chuyển cho đến khi cái đầu tiên bị loại bỏ.

+0

Tôi không quen thuộc với các nghi thức mở rộng các lớp bản địa trong Ruby. Điều này sẽ được coi là hình thức xấu như nó là trong javascript? – Duopixel

+2

Ruby on Rails có một số lượng lớn các phần mở rộng cho các lớp lõi của Ruby, vì vậy nó trở thành một loại truyền thống. Nó thực sự phụ thuộc. Có một đường thẳng giữa thông minh và * quá * thông minh. Nếu bạn thực hiện thao tác này ở nhiều nơi, nó sẽ có ý nghĩa. Nếu chỉ trong một, tôi sẽ gắn bó với những gì bạn có. – tadman

11

Có lẽ điều này có vẻ tốt hơn cho bạn:

a.insert(0, a.delete('c')) 
+0

Đây là giải pháp tuyệt vời khi mảng nguồn được đảm bảo là duy nhất. Câu trả lời được chấp nhận ở trên bao gồm nhiều kịch bản hơn; tuy nhiên, nếu mảng không chứa các bản sao thì việc thực hiện nhanh hơn và dễ dàng hơn so với IMHO ở trên. – danielricecodes

3

Cuối cùng tôi coi đó là sự thay thế có thể đọc được hầu hết để di chuyển một yếu tố vào phía trước:

if @admin_users.include?(current_user) 
    @admin_users.sort_by{|admin| admin == current_user ? 0 : 1} 
end 
0

Nếu tất cả các phần tử trong mảng là duy nhất bạn có thể sử dụng mảng số học:

> a = ['a', 'b', 'c'] 
=> ["a", "b", "c"] 
> a -= "c" 
=> ["a", "b"] 
> a = ["c"] + a 
=> ["c", "a", "b"] 
1

Thêm hai của tôi cent:

array.select{ |item| <condition> } | array 

Ưu điểm:

  • thể di chuyển nhiều mục phía trước mảng

Nhược điểm:

  • Điều này sẽ xóa tất cả các từ khóa trùng lặp trừ khi đó là kết quả mong muốn.

Ví dụ - Di chuyển tất cả lẻ số vào phía trước (và làm cho mảng duy nhất):

data = [1, 2, 3, 4, 3, 5, 1] 
data.select{ |item| item.odd? } | data 
# Short version: 
data.select(&:odd?) | data 

Kết quả:

[1, 3, 5, 2, 4] 
0

Một cách khác:

a = [1, 2, 3, 4] 
b = 3 

[b] + (a - [b]) 
=> [3, 1, 2, 4] 
Các vấn đề liên quan