2012-10-03 4 views
15

Auch von Javascript kommt das für mich scheußlich aussieht:ein Element an der Vorderseite eines Arrays in Ruby neu positionieren

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

Gibt es eine besser lesbare Art und Weise ein Element an der Vorderseite eines Arrays platzieren?

bearbeiten meine eigentliche Code:

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

der Frage ist nicht klar, Sie fragen nach einer besser lesbaren Möglichkeit, "ein Element an die Front eines Arrays zu platzieren" ('Array # insert (index, value)'?), aber das Beispiel verwendet 'delete 'und scheint so, als wollten Sie ein Drehung. – tokland

+0

Ich habe die Frage umformuliert. – Duopixel

+0

ok, jetzt ist es klar. Muss es in-Place-Update sein, obwohl? Warum gibst du kein neues Array zurück? – tokland

Antwort

5

Dies ist ein heikler Problem, als es scheint. I definiert die folgenden Tests:

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 von @Duopixel vorgeschlagen ist elegant, aber produziert [3, 2, 1] für den zweiten Test.

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

@tadman delete verwendet, aber dies löscht alle passenden Elemente, so dass der Ausgang des vierten Test ist [2, 1, 3, 1, 3].

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

    self 
    end 
end 

ich versucht, mit:

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

aber, dass der dritte Test nicht bestanden, weil delete_at nil nicht verarbeiten kann. Schließlich ließ ich mich auf:

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

Wer eine einfache Idee wie promote wusste so schwierig sein könnte?

+0

Es ist eine Weile her seit dieser Frage, aber das sieht gut aus! – Duopixel

+0

Danke! Ich konnte nicht glauben, wie weit eine solch einfache Methode mich durch das Kaninchenloch geführt hat. – dankohn

+0

Das ist gut. Nur eine Beobachtung: Ihr gewähltes 'promote 'setzt voraus, dass es nur das erste Vorkommen von' promited_element 'verschieben soll. 'Array # delete' entfernt alle Elemente, die dem Parameter entsprechen. Wäre es unangemessen, von "promote" ein orthogonales Verhalten zu erwarten und dass das Ergebnis des vierten Tests "[2, 2, 1, 3, 1, 3]" wäre? Um Ihre 'promote' als Komponente in einer Methode zu verwenden, die alle Vorkommen von' promited_element' verschiebt, müsste das Array wiederholt dupliziert und verglichen werden (denke ich ...). – Huliax

14

Vielleicht Array#rotate würde für Sie arbeiten:

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

Gut zu wissen. Es geht leider nicht darum, die letzten Elemente nach vorne zu legen, sondern ein beliebiges Element zu nehmen und nach vorne zu stellen. Ich habe meine Frage und Entschuldigung aktualisiert. – Duopixel

+0

Ich kannte diese Methode nicht. – Papipo

4

Wenn von "elegant" Sie besser lesbar bedeuten auch auf Kosten des Seins nicht-Standard , Sie könnten immer Ihre eigene Methode schreiben, die Array verbessert:

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

Beachten Sie, dass dies nur eine einzelne Instanz eines Werts repositionieren würde. Wenn im Array mehrere vorhanden sind, werden die nachfolgenden wahrscheinlich erst dann verschoben, wenn die erste entfernt wird.

+0

Ich bin nicht mit der Etikette der Erweiterung von nativen Klassen in Ruby vertraut. Würde dies als schlechte Form angesehen werden, wie es in Javascript ist? – Duopixel

+2

Ruby on Rails hat eine enorme Anzahl von Erweiterungen zu den Ruby-Kernklassen, so dass es eine Art Tradition geworden ist. Es kommt wirklich darauf an. Es gibt eine feine Grenze zwischen clever und * zu * clever. Wenn Sie diese Operation an vielen Stellen ausführen, wäre dies sinnvoll. Wenn in nur einem, würde ich bei dem bleiben, was Sie haben. – tadman

11

Vielleicht sieht es dir besser:

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

Dies ist eine großartige Lösung, wenn das Quell-Array garantiert eindeutig ist. Die oben akzeptierte Antwort deckt mehr Szenarien ab; Wenn das Array jedoch keine Duplikate enthält, ist dies schneller und einfacher zu implementieren als das oben genannte - IMHO natürlich. – danielricecodes

3

Am Ende hielt ich dies die lesbare Alternative ein Element nach vorne zu bewegen:

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

Wenn alle Elemente im Array sind einzigartig Sie Array-Arithmetik verwenden können:

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

meine zwei Cent hinzu:

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

Pro:

  • mehrere Elemente verschieben vor dem Array

Nachteile:

  • Dies wird alle Duplikate entfernen, es sei denn, es ist das gewünschte Ergebnis.

Beispiel - Verschieben Sie alle ungerade Zahlen nach vorne (und machen Array einmalig):

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

Ergebnis:

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

Ein anderer Weg:

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

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