Pytanie Jaki jest najlepszy sposób na pocięcie łańcucha na kawałki o określonej długości w Ruby?


Szukałem eleganckiego i wydajnego sposobu dzielenia łańcucha na podciągi o określonej długości w Ruby.

Jak dotąd najlepsze, co mogłem wymyślić, to:

def chunk(string, size)
  (0..(string.length-1)/size).map{|i|string[i*size,size]}
end

>> chunk("abcdef",3)
=> ["abc", "def"]
>> chunk("abcde",3)
=> ["abc", "de"]
>> chunk("abc",3)
=> ["abc"]
>> chunk("ab",3)
=> ["ab"]
>> chunk("",3)
=> []

Możesz chcieć chunk("", n) wracać [""] zamiast []. Jeśli tak, dodaj to jako pierwszą linię metody:

return [""] if string.empty?

Czy poleciłbyś jakieś lepsze rozwiązanie?

Edytować

Dzięki Jeremy Ruten za to eleganckie i wydajne rozwiązanie:

def chunk(string, size)
    string.scan(/.{1,#{size}}/)
end

76
2018-04-16 01:06


pochodzenie




Odpowiedzi:


Posługiwać się String#scan:

>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,4}/)
=> ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx", "yz"]
>> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,3}/)
=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]

137
2018-04-16 01:26



Ok, teraz jest świetnie! Wiedziałem, że musi być lepszy sposób. Wielkie dzięki Jeremy Ruten. - MiniQuark
def chunk (string, size); string.scan (/. {1, # {rozmiar}} /); koniec - MiniQuark
Wow, teraz czuję się głupio. Nigdy nawet nie próbowałem sprawdzić, jak działa skanowanie. - Chuck
Uważaj na to rozwiązanie; to jest wyrażenie regularne, a /. bit to oznacza, że ​​będzie zawierał wszystkie znaki Z WYJĄTKIEM nowych linii \n. Jeśli chcesz dodać nowe linie, użyj string.scan(/.{4}/m) - professormeowingtons
Co za sprytne rozwiązanie! Uwielbiam wyrazy regularne, ale nie użyłbym kwantyfikatora w tym celu. Dziękuję Jeremy Ruten - Cec


Oto inny sposób na zrobienie tego:

"abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s }

=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]


16
2018-02-04 20:04



Alternatywnie: "abcdefghijklmnopqrstuvwxyz".chars.each_slice(3).map(&:join) - Finbarr
Podoba mi się ten, ponieważ działa na ciągach zawierających znaki nowej linii. - Steve Davis
To powinno być akceptowane rozwiązanie. Użycie skanowania może spowodować usunięcie ostatniego tokena, jeśli długość nie będzie równa wzór. - count0


Myślę, że jest to najbardziej wydajne rozwiązanie, jeśli wiesz, że twój ciąg jest wielokrotnością wielkości kawałka

def chunk(string, size)
    (string.length / size).times.collect { |i| string[i * size, size] }
end

i na części

def parts(string, count)
    size = string.length / count
    count.times.collect { |i| string[i * size, size] }
end

2
2017-07-26 14:00



Twój ciąg nie musi być wielokrotnością wielkości kawałka, jeśli go zastąpisz string.length / size z (string.length + size - 1) / size - ten wzór jest powszechny w kodzie C, który musi zajmować się obcięciem całkowitym. - nitrogen


test.split(/(...)/).reject {|v| v.empty?}

Odrzucenie jest konieczne, ponieważ w przeciwnym razie zawiera pustą przestrzeń między zestawami. Mój regex-fu nie jest w stanie zobaczyć, jak to naprawić z mojej głowy.


1
2018-04-16 01:20



przy podejrzeniu skanowania zapomnisz o niesparowanych caracteres, czyli: jeśli spróbujesz z 10-ciometrowym fragmentem struny na 3 częściach, będziesz miał 3 części i jeden element zostanie upuszczony, twoje aproach tego nie zrobią, więc najlepiej. - vinicius gati


Czy są jakieś inne ograniczenia, które masz na myśli? W przeciwnym razie byłbym bardzo kuszony, by zrobić coś prostego

[0..10].each {
   str[(i*w),w]
}

0
2018-04-16 01:15



Nie mam żadnych ograniczeń, oprócz czegoś prostego, eleganckiego i skutecznego. Podoba mi się twój pomysł, ale czy mógłbyś przetłumaczyć go na metodę? [0..10] prawdopodobnie stałoby się nieco bardziej złożone. - MiniQuark
Naprawiłem mój przykład, aby użyć str [iw, w] zamiast str [iw ... (i + 1) * w]. Tx - MiniQuark
Powinno to być (1..10) .collect zamiast [0..10] .each. [1..10] to tablica składająca się z jednego elementu - zakresu. (1..10) to sam zakres. I + każdy + zwraca oryginalną kolekcję, do której jest wywoływana ([1..10] w tym przypadku), a nie wartości zwracane przez blok. Chcemy + mapę + tutaj. - Chuck