2012-10-03 11 views
15

Nawet pochodzących z javascript wygląda to okropne dla mnie:Założyć element z przodu tablicy w Ruby

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

Czy jest bardziej czytelny sposób umieszczenie elementu do przedniej tablicy?

Edit mój rzeczywisty kod:

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

the pytanie nie jest jasne, pytasz o bardziej czytelny sposób "umieszczania elementu z przodu tablicy" ('Array # insert (index, value)'?), ale w przykładzie użyto 'delete' i wydaje się, że chciałeś obrót. – tokland

+0

Przeformułowałem pytanie. – Duopixel

+0

OK, teraz jest jasne. Czy ma to być aktualizacja na miejscu? dlaczego nie zwrócić nowej tablicy? – tokland

Odpowiedz

5

To jest trudniejsze problemem niż to wydaje się. I zdefiniowano następujące testy:

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 proponowany przez @Duopixel jest elegancki, ale produkuje [3, 2, 1] dla drugiego testu.

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

@tadman wykorzystuje delete, ale ta usuwa wszystkie elementy pasujące, więc wyjście czwartego testu jest [2, 1, 3, 1, 3].

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

    self 
    end 
end 

Próbowałem za pomocą:

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

Ale udało trzecią próbę ponieważ delete_at nie może obsłużyć nil. Wreszcie zdecydowałem się na:

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

Kto wiedział prosty pomysł jak promote może być tak skomplikowane?

+0

Minęło trochę czasu od tego pytania, ale to wygląda dobrze! – Duopixel

+0

Dzięki! Nie mogłem uwierzyć, jak daleko w dół króliczej dziury zabrała mnie taka prosta, pozorna metoda. – dankohn

+0

To jest dobre. Po prostu spostrzeżenie: wybrana przez ciebie 'promuj' zakłada, że ​​ma tylko przesunąć pierwsze wystąpienie' promoted_element'. 'Array # delete' usuwa wszystkie elementy pasujące do parametru. Czy nieuzasadnione byłoby oczekiwanie zachowania ortogonalnego z 'promote' i że wynikiem twojego czwartego testu byłoby '[2, 2, 1, 3, 1, 3]'? Używanie 'promowania' jako składnika w metodzie, która przenosi wszystkie wystąpienia' promoted_element' wymagałoby wielokrotnego duplikowania tablicy i porównywania (myślę ...). – Huliax

14

Może Array#rotate będzie pracować dla Ciebie:

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

Dobrze wiedzieć. Niestety nie jest to kwestia umieszczenia ostatniego elementu (elementów) na froncie, bierze dowolny element i umieszcza go na pierwszym planie. Zaktualizowałem moje pytanie i przeprosiłem. – Duopixel

+0

Nie znałem tej metody. – Papipo

4

Jeśli przez „elegancki” znaczy bardziej czytelny nawet kosztem będącego nietypowych , zawsze możesz napisać własną metodę, która zwiększa 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"] 

Należy pamiętać, że będzie to tylko zmiana położenia pojedynczego wystąpienia wartości. Jeśli w tablicy jest kilka, kolejne prawdopodobnie nie zostaną przeniesione, dopóki pierwsza nie zostanie usunięta.

+0

Nie jestem zaznajomiony z etykietą rozszerzania klas rodzimych w Ruby. Czy byłoby to uznane za złą formę, tak jak w javascript? – Duopixel

+2

Ruby on Rails ma ogromną liczbę rozszerzeń do klas Ruby, więc stała się swego rodzaju tradycją. To naprawdę zależy. Jest cienka granica między sprytnym a * zbyt * sprytnym. Jeśli wykonasz tę operację w wielu miejscach, miałoby to sens. Gdybym w jednym z nich trzymał się tego, co masz. – tadman

11

Może wygląda to lepiej dla Ciebie:

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

Jest to doskonałe rozwiązanie, gdy macierz źródłowa ma być unikalna. Przyjęta wyżej odpowiedź obejmuje więcej scenariuszy; jeśli jednak tablica nie będzie zawierała duplikatów, jest to szybsze i łatwiejsze do wdrożenia niż powyższe - IMHO oczywiście. – danielricecodes

3

W końcu uznałem to najbardziej czytelny alternatywą dla ruchu elementu do przodu:

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

Jeśli wszystkie elementy w tablicy są unikalne można użyć tablicy arytmetyki:

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

Dodawanie moje dwa centy:

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

Plusy:

  • można przenieść kilka pozycji z przodu tablicy

Minusy:

  • Spowoduje to usunięcie wszystkich duplikatów, chyba że jest to pożądany wynik.

przykład - przenieść wszystkie nieparzystych numery do przodu (i jednocześnie macierz unikalne):

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

Wynik:

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

inny sposób:

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

[b] + (a - [b]) 
=> [3, 1, 2, 4]