2010-12-22 4 views
6

Zum Beispiel die Worte „Stack“, Ich möchte wie ein Array erhalten:Was ist der beste Weg, um eine Zeichenfolge aufzuteilen, um alle Teilstrings von Ruby zu erhalten?

['s', 'st', 'sta', ... 'stack', 't', 'ta', ... , 'c', 'ck', 'k'] 

ich dies durch einen solchen Code tat:

def split_word(str) 
    result = [] 
    chas = str.split("") 
    len = chas.size 
    (0..len-1).each do |i| 
    (i..len-1).each do |j| 
     result.push(chas[i..j].join) 
    end 
    end 
    result.uniq 
end 

Gibt es eine bessere und saubere Art und Weise, das zu tun? Vielen Dank.

+0

Mit oder ohne Duplikate? –

+0

ohne Duplikate, danke! –

+0

Allgemeiner Hinweis: Bessere Verwendung der Karte (funktional) anstelle des Musters "init leer + iterieren + push + return" (Imperativ) – tokland

Antwort

11
def split_word s 
    (0..s.length).inject([]){|ai,i| 
    (1..s.length - i).inject(ai){|aj,j| 
     aj << s[i,j] 
    } 
    }.uniq 
end 

Und Sie können auch anstelle von Array für das Ergebnis mit Set betrachten.

PS: Hier ist eine andere Idee, basierend auf Array Produkt:

def split_word s 
    indices = (0...s.length).to_a 
    indices.product(indices).reject{|i,j| i > j}.map{|i,j| s[i..j]}.uniq 
end 
+0

was für eine coole Lösung! –

+0

feste zweite Lösung nach einem Vorschlag von Benutzer @steel –

2

Glauben Sie nicht.

Hier ist meine Version versucht:

def split_word(str) 
    length = str.length - 1 
    [].tap do |result| 
    0.upto(length) do |i| 
     length.downto(i) do |j| 
     substring = str[i..j] 
     result << substring unless result.include?(substring) 
     end 
    end 
    end 
end 
+0

Gut! Es funktioniert und sieht sauberer aus. –

3

ich schreiben würde:

def split_word(s) 
    0.upto(s.length - 1).flat_map do |start| 
    1.upto(s.length - start).map do |length| 
     s[start, length] 
    end 
    end.uniq 
end 

groups = split_word("stack") 
# ["s", "st", "sta", "stac", "stack", "t", "ta", "tac", "tack", "a", "ac", "ack", "c", "ck", "k"] 

Es ist in der Regel klarer und kompakter map (funktionale) zu verwenden, statt der Muster Init leer + jedes + anhängen + zurückgeben (Imperativ).

+0

Ja, Sie haben Recht tokland.Ich bevorzuge funktionale Muster als imperative Muster auch. Danke für Ihren Vorschlag! Und welches Muster hat Ihrer Meinung nach die bessere Leistung? –

+0

@ pake007: Nun, ich sagte, dass es normalerweise schneller ist, aber hier war ich wegen der Verflachung und der Nicht-Faulheit von Rubys Arrays nicht so sicher. Wie auch immer, schwitzen Sie nicht für die Leistung, der Unterschied ist wahrscheinlich minimal. – tokland

0

Viel später, aber das habe ich von der Neuformatierung Ihres Codes bekommen.

def substrings(string) 
    siz = string.length 
    answer = [] 

    (0..siz-1).each do |n| 
    (n..siz-1).each do |i| 
     answer << string[n..i] 
    end 
    end 
    answer 
end 
2
def substrings(str) 
    output = [] 
    (0...str.length).each do |i| 
    (i...str.length).each do |j| 
     output << str[i..j] 
    end 
    end 
    output 
end 

dies ist nur ein aufgeräumt Version Ihrer Methode und es funktioniert mit weniger Schritten =)

2
def substrings(str) 
    (0...str.length).map do |i| 
    (i...str.length).each { |j| str[i..j]} 
    end 
end 

Nur ein weiterer Weg, es zu tun, die ein wenig klarer zu mir liest.

Verwandte Themen