[RUBY] Obtenez "2-4, 7, 9" de [4, 7, 9, 2, 3]

introduction

Cet article a été rédigé en consultant les articles suivants. J'ai essayé de résoudre un problème de programmation simple en utilisant la correspondance de modèle Ruby --Qiita

L'article ci-dessus

"1, 5, 10-12, 15, 18-20"

À partir de la chaîne de caractères

[1, 5, 10, 11, 12, 15, 18, 19, 20]

Il décrit comment obtenir le tableau dans Ruby.

D'un autre côté, cet article est le contraire. En d'autres termes, lorsqu'un tableau d'entiers positifs est donné, il est trié, puis les numéros de série sont reliés par des tirets pour créer une chaîne de caractères séparés par des virgules.

Ce processus est utilisé, par exemple, pour créer la notation de numéro de page pour un index de livre.

De plus, dans une telle notation,

Il existe deux styles possibles, mais dans cet article, nous prendrons le premier (écrire également sur le second).

code

J'ai également écrit le code de test en utilisant test-unit.

require "test/unit"

def gather(array)
  array.uniq.sort
    .chunk_while{ |a, b| a.succ == b }
    .map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }
    .join(", ")
end

#Ci-dessous le code de test
#Si vous exécutez le script, le test décrit ci-dessous sera effectué automatiquement.

class TestGather < Test::Unit::TestCase
  test "Démembré" do
    assert_equal "1, 3, 5", gather([3, 5, 1])
  end

  test "Il y a duplication" do
    assert_equal "2, 4", gather([4, 2, 2, 4, 4])
  end

  test "Résumé" do
    assert_equal "1-2", gather([2, 1])
    assert_equal "1-3", gather([2, 3, 1])
    assert_equal "2-4, 7, 9, 11-12",
      gather([12, 11, 7, 9, 2, 4, 3])
  end
end

Commentaire

Trier sans duplication

En premier

array.uniq.sort

En conséquence, la duplication est omise et le tri est effectué. Je ne pense pas qu'il faille beaucoup d'explications. Cependant, compte tenu de l'efficacité, il convient de noter qu'il vaut mieux d'abord ʻuniq puis trier` [^ uniq-sort].

[^ uniq-sort]: Pas très différent s'il y a peu de doublons. Cependant, il peut être doctrinal de se souvenir de «.uniq.sort».

Catalyser le numéro de série

Ensuite, le cœur de ce code est

chunk_while{ |a, b| a.succ == b }

Partie de. En fait, la référence officielle [Enumerable # chunk_ While](https://docs.ruby-lang.org/ja/2.7.0/method/Enumerable/i/chunk_ while.html) a un exemple qui ressemble exactement au code de cet article. Bien que cela ait été fait.

chunk_ while est une méthode très intéressante de type Ruby. Qui est chunk_ while?

Pour un objet Enumerable tel qu'un tableau, il s'agit d'une décision de lécher </ ruby> les éléments du bord et de se connecter ou de se déconnecter entre deux éléments adjacents. C'est une méthode qui décide en fonction des critères ** et crée un morceau de cette façon. Cependant, la valeur de retour est Enumerator, pas un tableau de catamari. Je n'expliquerai pas ce qu'est un Enumerator dans cet article, mais comme c'est un objet Enumerable, si vous to_a, ce sera un tableau de catamari.

Par exemple

[2, 6, 0, 3, 5, 8]

Disons qu'il y a un arrangement appelé, et nous voulons le diviser en catégories paires et impaires. En d'autres termes

[[2, 6, 0], [3, 5], [8]]

Je veux un tableau appelé. L'important est que seuls les ** égales et cotes ** adjacentes ** soient ** catalysées. Par conséquent, le dernier "8" n'est pas combiné avec "2, 6, 0", mais est un point de saut.

Au fait, comment écrire l'expression conditionnelle selon laquelle les deux entiers ʻa et b` sont "les deux sont pairs ou les deux sont impairs"? Une façon est

(a.even? && b.even?) || (a.odd? && b.odd?)

Mais vous n'avez pas à faire ce genre de problème. Si vous remarquez que "la différence entre pair et pair est paire", "la différence entre impair et impair est paire" et "la différence entre pair et impair est impaire"

(a - b).even?

Je sais que ça va.

Maintenant, pour obtenir un tableau de catamari pairs et impairs adjacents, écrivez comme suit en utilisant chunk_ while.

numbers = [2, 6, 0, 3, 5, 8]

p numbers.chunk_while{ |a, b| (a - b).even? }.to_a
# => [[2, 6, 0], [3, 5], [8]]

Comment fonctionne ce chunk_ while? chunk_ while utilise ʻeachpour récupérer chaque élément du récepteur. Tout d'abord, faites-le pour extraire les deux éléments. Dans ce cas, "2" et "6" sont récupérés. Passez ces deux au bloc et évaluez le bloc. Alors "(2-6). Même?" Est-ce vrai. A ce moment,chunk_ whiledécide," Hmmmm, alors je ne me déconnecterai pas entre "2" et "6". " Ensuite, passez le «6» déjà récupéré et le «0» récupéré suivant au bloc. Encore une fois, le bloc renvoie vrai, donc il ne se déconnecte pas. De même, transmettez «0» et «3» au bloc, qui renvoie false. Ensuite,chunk_ while` décide," Okay! Coupe-le ici! ".

La même chose s'applique ci-dessous.

La façon dont chunk_ while prend deux éléments adjacents tout en décalant leurs positions un par un est similaire à ʻeach_cons (2). ʻEach_cons (2) ne prend que deux éléments, tandis que chunk_ while prend deux éléments pour déterminer où rejoindre / déconnecter.

Maintenant que vous savez comment fonctionne chunk_ while

chunk_while{ |a, b| a.succ == b }

Considérons. ʻInteger # succ est une méthode qui renvoie un entier obtenu en ajoutant 1 à lui-même. ʻA.succ == b exprime la condition que "après ʻa est b`".

Résumez les numéros de série avec des tirets

Suivant

map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }

Est facile.

Oups, un mot avant ça. Puisque la valeur de retour de chunk_ While est un objet Enumerator, il n'est pas nécessaire d'en faire un tableau avec to_a, et map peut être continué telle quelle.

Au fait, concernant ce bloc map, si chaque catamari a une longueur de 1, il est laissé tel quel, et s'il est égal ou supérieur à 2, le début et la fin sont reliés par un trait d'union.

Par exemple, «[7]» est laissé comme «[7]» et «[2, 3, 4]» est remplacé par «« 2-4 »». Eh bien, le tableau converti est un peu désagréable car les éléments sont des tableaux et des chaînes. Non, cela ne pose aucun problème (décrit plus loin), mais il est certain que ce sera un peu mou.

Connectez-vous avec une virgule

finalement

join(", ")

En finir avec. Eh bien, il ne s'agit que de connecter les éléments avec ", ", mais pour être sûr que vous avez dit dans le paragraphe précédent que "les éléments peuvent être des tableaux ou des chaînes, peu importe", Array # join nécessite une compréhension précise.

join renvoie une chaîne connectée avec un argument intermédiaire si tous les éléments sont des chaînes. Si certains des éléments ne sont pas des chaînes, ils sont d'abord convertis en chaînes avant d'être connectés, mais s'ils sont des tableaux, ils sont convertis en caractères en utilisant join au lieu de to_s. À ce moment-là, les mêmes arguments que la «jointure» d'origine sont utilisés pour la «jointure».

Alors

p [1, [2, 3]].join("-") # => "1-2-3"

devenir de cette façon.

Avec ce qui précède, le fonctionnement est parfaitement compris.

variation

Comme je l'ai prédit dans la section "Introduction", comment puis-je le modifier pour qu'il ne soit pas groupé avec des tirets alors qu'il n'y a que deux nombres consécutifs tels que "4,5"?

En d'autres termes

p gather([1, 2, 4, 5, 6]) # => "1, 2, 4-6"

Comment le faire

En fait, l'exemple de code pour [Enumerable # chunk_ While](https://docs.ruby-lang.org/ja/2.7.0/method/Enumerable/i/chunk_ While.html) est écrit selon ce style.

Faire cela

.map{ |c| (c.size == 1) ? c : "#{c[0]}-#{c[-1]}" }

À

.map{ |c| (c.size < 3) ? c : "#{c[0]}-#{c[-1]}" }

Vous pouvez le changer en. Il ressort clairement des spécifications de «jointure» déjà mentionnées que seule cette modification est requise.

De côté

Il est malheureux que la bibliothèque de tests unitaires similaires de Ruby reste divisée en unité de test et minitest.

A mes yeux, le test-unit semble être meilleur [^ tu], mais Rails a adopté minitest (une modification magique de celui-ci?), Donc minitest peut être supérieur au monde. inconnue.

[^ tu]: Avec test-unit, vous pouvez donner le nom du test sous forme de chaîne de caractères comme dans cet article, et vous pouvez également effectuer des tests basés sur les données.

e? RSpec? Non, je n'ai pas l'impression de pouvoir l'écrire parce que cela m'est trop difficile à comprendre. Qu'est-ce que «it»?

Recommended Posts

Obtenez "2-4, 7, 9" de [4, 7, 9, 2, 3]
Obtenir TypeElement et TypeMirror à partir de la classe
Obtenez des données GTFS à partir du graphique d'OpenTripPlanner
[Kotlin] Trois façons d'obtenir un cours depuis KClass
Obtenir des informations sur l'appelant à partir de la trace de la pile (Java)
Obtenir les noms de colonnes d'une instance de Model
[Java] Obtenir des informations sur les balises à partir de fichiers musicaux
Obtenir l'historique du serveur Zabbix en Java
Obtenez les prévisions météo de Open Weather Map with Rails
Obtenir la valeur des paramètres d'URL avec Get Mapping-LocalDate-
IntelliJ à partir de 1
Comment obtenir un heapdump à partir d'un conteneur Docker
Comment obtenir une classe depuis Element en Java
Obtenez unixtime (secondes) de ZonedDateTime dans Scala / Java
Obtention de l'objet Locale
[Java] Obtenir plusieurs valeurs à partir d'une valeur de retour
Calendar.MONTH commence à 0
[Java] Récupère des valeurs de manière aléatoire dans un tableau
Obtenez Enum en retirant du contenu
Comment obtenir l'optimisation SIMD pour HotSpot JavaVM
◆ Obtenez l'API créée par Spring Boot à partir de React
[Java] Obtenir KFunction à partir de la méthode / du constructeur en Java [Kotlin]
Test de StringBoot
[Swift5] Obtenez des informations de tableau (individuelles) à partir de JSON à l'aide de la bibliothèque'SwiftyJSON '
Comment obtenir JDK etc. depuis Oracle avec CLI