1
votes

Ruby each_slice en sous-tableaux et définir les valeurs d'élément par défaut si le dernier sous-tableau est de taille différente des autres sous-tableaux

J'ai un tableau:

array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
array.each_slice(3).to_a
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7"]]

Dans lequel je veux convertir:

 [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]

J'ai essayé avec each_slice code > mais renvoie le sous-tableau sans les valeurs false :

["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]

des méthodes Ruby prenant en charge le comportement souhaité?

p >


0 commentaires

5 Réponses :


2
votes

#tap dans le tableau résultant et #fill le dernier élément avec le nombre nécessaire d'éléments false :

array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
array
  .each_slice(3)
  .to_a
  .tap { |a| a.last.fill(false, a.last.length, 3 - a.last.length) }


0 commentaires

0
votes

Autre option

ary = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
n = 3
ary.then { |ary| ary + Array.new(- ary.size % n){false} }.each_slice(n)#.to_a

Elle remplit les éléments manquants avant de découper.


0 commentaires

2
votes
a = enum1.next
  #=> ["7"] 
enum2 = n.times.map
  #=> #<Enumerator: #<Enumerator: 3:times>:map>
enum2.each { |i| a.fetch(i, false) }
  #=> ["7", false, false]

8 commentaires

Pour les lecteurs qui, comme moi, n'ont jamais vu un vote pour "supprimer" (par opposition à "fermer"), c'est expliqué ici . J'ai passé en revue les critères de suppression ou de vote pour supprimer et je ne trouve rien qui semble s'appliquer, donc je suis perplexe.


Par hasard, j'ai vu votre commentaire, Cary. On ne sait pas ce qui vous a perplexe (je ne vois pas de votes serrés en attente) mais je me demande si c'était le vote négatif. C'était le mien, et mes excuses - je pensais que c'était explicite. Les réponses basées uniquement sur le code sont un pari risqué sur Stack Overflow - elles font l'objet de controverses sur Meta . À mon avis, l'opportunité d'apprentissage pour les futurs lecteurs (sans compter le PO) serait considérablement élargie si un texte explicatif initial était fourni.


Je ne fais pas Ruby, mais je me demande s'il y a du matériel supplémentaire que vous pourriez ajouter pour préface?


Merci d'être propriétaire, @halfer. J'ai édité, mais je pense que dans ce cas, le besoin d'une explication est limite. Pour un sou, pour une livre, j'ai donc fourni l'une de mes explications «atroces». Je n'étais pas perplexe par le vote défavorable, mais par le "supprimer (1)" (peut-être que je peux le voir). Peut-être qu'un modérateur l'a fait pour la même raison que vous avez voté contre. Soit dit en passant, ma pratique est de toujours laisser un commentaire si je déconseille ou vote pour clôturer. Si je pense qu'une réponse a du mérite mais nécessite une explication plus complète, je laisse un commentaire à cet effet, mais je déconseille rarement si le PO ne prend aucune mesure.


Pas de soucis. Oui, je ne vois pas de vote de suppression (que ce soit sur la question ou votre réponse) - cela peut être un privilège de 20k Merci pour les modifications - c'est exactement ce que je recherche.


J'ai personnellement tendance à commenter mes votes négatifs, mais je n'oblige pas les autres à le faire - il y a parfois une tendance à la vengeance contre le vote sur la plate-forme, et je peux certainement comprendre pourquoi les nouveaux utilisateurs en ont peur. Je ne l'ai pas beaucoup expérimenté, mais j'ai tendance à obtenir le plus, euh, de "retours robustes" de plus de 20 000 utilisateurs, ce qui explique peut-être pourquoi je n'ai pas fait de commentaire auparavant.


@halfer, j'ai vécu ce que je crois être un vote par vengeance. Curieusement, cela amène un sourire entendu sur mon visage. Quand il y a un motif, je signale.


Oh oui, ça ne me dérange pas, et je signale volontiers aussi :-)



3
votes

Vous pouvez compter le nombre d'éléments de remplissage nécessaires avec:

> array = ["1", "b", "0", "7", "9", "2", "a", "a", "5", "7"]
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]

> array << '1'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", false]]

> array << '2'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", "2"]]

> array << '3'
> padded_each_slice(array).to_a
=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", "1", "2"], ["3", false, false]]

En chaînant les énumérateurs, il est possible d'effectuer le remplissage comme dans Réponse d'iGian sans créer de tableau intermédiaire ni toucher le tableau d'origine:

def padded_each_slice(array, chunk_size = 3, pad = false)
  (array.each + Array.new(-array.size % chunk_size, pad)).each_slice(chunk_size)
end

Faites-en une méthode:

> [1, 2, 3] + [4]
=> [1, 2, 3, 4]
> [1,2,3].each + [4]
=> #<Enumerator::Chain: ...>
> ([1,2,3].each + [4]).to_a
=> [1, 2, 3, 4]

Et mettons-le à l'épreuve:

-array.size % chunk_size

Peut-être pas si beau, mais je voulais ajouter une réponse avec des énumérateurs enchaînés, ce qui sont une nouvelle fonctionnalité introduite dans Ruby 2.6.

Inclut une amélioration suggérée par @Stefan dans les commentaires .


11 commentaires

Utiliser -array.size% chunk_size pour calculer le nombre d'éléments de remplissage vous permet de vous débarrasser de la condition.


Cette réponse, ainsi que celle de @ iGian, a la faiblesse de construire un tableau intermédiaire qui est au moins aussi grand que celui d'origine.


@CarySwoveland cette réponse ne crée pas un tel tableau: + est appelé sur chaque énumérateur du tableau, pas sur le tableau lui-même.


BTW, il pourrait être plus propre d'utiliser array.chain (autre) au lieu de array.each + other .


@Stefan, merci. Kimmo, mon commentaire était le résultat d'une lecture paresseuse de votre réponse, dont je m'excuse. Je n'étais pas au courant de Enumerator # + et Enumerable # chain , mais comme je trouvé qu'ils ont été introduits dans la v2.6, ce n'est peut-être pas trop surprenant. (Vous pourriez mentionner qu'ils sont nouveaux dans votre réponse.) ...


... Supposons que e = [1,2] .each + [3]; e.class # => Enumérateur :: Chaîne; e.method (: next) # => # , montrant que e hérite de Enumerator # next . Je m'attendrais donc à e.next # => 1 , mais e.next # => TypeError (mauvaise chaîne de type d'argument (énumérateur attendu)) . Pouvez-vous ou @Stefan ou quelqu'un d'autre expliquer?


@CarySwoveland ressemble à un bug pour moi. e = ([1,2] .each + [3]). each ou e = [1,2] .chain ([3]). each fonctionne comme attendu.


@Stefan, laissez-moi réfléchir. Si je (ou vous ou quelqu'un d'autre) ne peut pas raisonner que ce n'est pas un bogue, je le signalerai, auquel cas je laisserai ici un commentaire contenant un lien vers le rapport.


À l'origine, j'avais (array.each + other.each) .each_slice mais j'ai accidentellement laissé ce dernier .each, mais cela semblait fonctionner.


@CarySwoveland signale-le, s'il vous plaît. La documentation de Enumerable :: Chain prétend que c'est "[…] une chaîne d'énumérables qui fonctionne comme un seul énumérateur" . Mais cela soulève des exceptions pour presque toutes les méthodes Enumerator ( next , peek , each_with_index etc.). Je suis assez habitué aux bogues dans les logiciels occasionnels, même les systèmes d'exploitation, mais cela me déroute toujours de voir des implémentations cassées (ou bâclées) dans les langages de programmation.


@Stefan, d'accord, je le ferai demain. btw, je pense que vous voulez dire each_index .



0
votes

Autre option:

> array.each_slice(3).to_a.map{|e| Array.new(3){e.shift }}
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", nil, nil]]

pour remplir l'élément manquant avec nil

> array.each_slice(3).to_a.map{|e| Array.new(3){e.empty? ? false: e.shift }}
#=> [["1", "b", "0"], ["7", "9", "2"], ["a", "a", "5"], ["7", false, false]]


0 commentaires