2009-04-16 10 views
73

Je cherchais un moyen élégant et efficace de couper une corde en sous-chaînes d'une longueur donnée en Ruby.Quelle est la meilleure façon de couper une chaîne en morceaux d'une longueur donnée dans Ruby?

Jusqu'à présent, le mieux que je pouvais trouver est la suivante:

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) 
=> [] 

Vous voudrez peut-être revenir chunk("", n)[""] au lieu de []. Si c'est le cas, il suffit d'ajouter ceci comme première ligne de la méthode:

return [""] if string.empty? 

Recommanderiez-vous la meilleure solution?

Modifier

Merci à Jeremy Ruten pour cette solution élégante et efficace:

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

Répondre

132

Utilisation 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"] 
+0

Ok, maintenant c'est excellent! Je savais qu'il devait y avoir un meilleur moyen. Merci beaucoup Jeremy Ruten. – MiniQuark

+1

def morceau (chaîne, taille); string.scan (/. {1, # {taille}} /); fin – MiniQuark

+1

Wow, je me sens stupide maintenant. Je n'ai même jamais pris la peine de vérifier le fonctionnement du scan. – Chuck

0

Y at-il d'autres contraintes que vous avez à l'esprit? Sinon, je serais terriblement tenté de faire quelque chose de simple comme

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

Je n'ai pas vraiment aucune contrainte, en dehors d'avoir quelque chose de simple, élégant et efficace. J'aime votre idée, mais pourriez-vous la traduire en une méthode s'il vous plaît? Le [0..10] deviendrait probablement légèrement plus complexe. – MiniQuark

+0

J'ai corrigé mon exemple pour utiliser str [i * w, w] au lieu de str [i * w ... (i + 1) * w]. Tx – MiniQuark

+0

Cela devrait être (1..10) .collect plutôt que [0..10] .each. [1..10] est un tableau constitué d'un élément - une plage. (1..10) est la plage elle-même. Et + chaque + renvoie la collection d'origine sur laquelle elle est appelée ([1..10] dans ce cas) plutôt que les valeurs renvoyées par le bloc. Nous voulons + map + ici. – Chuck

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

Le rejet est nécessaire, car il comprend par ailleurs l'espace vide entre les séries. Mon regex-fu n'est pas tout à fait en mesure de voir comment résoudre ce problème de ma tête.

+0

l'approche de balayage oubliera les caractères non appariés, c'est-à-dire: si vous essayez avec une tranche de 10 longueurs sur 3 parties, vous aurez 3 parties et 1 élément sera abandonné, votre approche ne le fera pas. –

15

Voici une autre façon de le faire:

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

=> [ "abc", "DEF", "GHI", "JKL", « mno », "pqr", "STU", "vwx", "yz"]

+11

Alternativement: '" abcdefghijklmnopqrstuvwxyz ".chars.each_slice (3) .map (&: join)' – Finbarr

+3

J'aime celui-ci car il fonctionne sur les chaînes contenant des retours à la ligne. –

+1

Cela devrait être la solution acceptée. L'utilisation de l'analyse peut faire tomber le dernier jeton si la longueur ne correspond pas à _pattern_. – count0

2

Je pense que c'est la solution la plus efficace si vous savez que votre chaîne est un multiple de la taille du morceau

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

et pièces

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

Votre chaîne ne doit pas être un multiple de taille de bloc si vous remplacez 'string.length/size' par' (string.length + size - 1)/size' - ce modèle est commun dans le code C qui doit traiter avec la troncature d'entier. – nitrogen

Questions connexes