Aller au contenu

Tri croissant des numéro de voirie


Messages recommandés

Bonjour,

 

C'est pas propre à AutoCAD, mais j'ai un truc qui a l'air tout con, mais je ne m'en sort pas.

Dans une datagridview, j'affiche des adresses avec le nom de la rue dans une colonne et le numéro de la rue dans une autre colonne.

J'aimerais trié par ordre croissant mes adresses : Nom de rue puis n° croissant.

Le problème c'est que j'ai des numéros stockés en texte car j'ai par exemple 1 2 5, mais aussi 3B 3C 3D 12B ...

J'aimerais par exemple que 1B soit après 1 mais avant 2 et aussi que 10 soit après 9 et avant 11 et pas coincé entre 1 et 100 (tri alphabétique simple).

Je pense que je vais être obligé de scinder tout ça en chiffre et lettre avant de trier, mais s'il y a une solution plus simple, je suis preneur.

 

Merci, Olivier

Lien vers le commentaire
Partager sur d’autres sites

Salut Pat,

 

Pour la partie numérique pas de souci, mais il faut d'abord scindé la partie alpha, puis faire un tri sur les 3 colonnes, ce qui est moins évident, surtout avec la partie alpha qui peut être vide.

Je pense avoir solutionner en créant une autre colonne qui concatène le nom de la rue puis _ puis la partie numérique du numéro sur 4 caractères puis _ puis la partie alpha ou rien si vide. par ex ça donne : RUE ALEXANDRE DUMAS_0012_B et ça je le trie comme du texte.

Si je mets des espaces au lieu des underscore ça ne fonctionne pas.

Olivier

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

pour le tri des numéros de rue, tu peux utiliser un comparateur du style :

 

EDIT : code corrigé

    class StreetNumberComparer : IComparer<string>
   {
       public int Compare(string x, string y)
       {
           var regex = new Regex(@"(?i)^([0-9]+)([A-Z]*)$");
           var matchX = regex.Match(x);
           if (!matchX.Success)
               throw new FormatException($"Numéro de rue non valide : {x}");
           var matchY = regex.Match(y);
           if (!matchY.Success)
               throw new FormatException($"Numéro de rue non valide : {y}");
           var compareNumber = int.Parse(matchX.Groups[1].Value).CompareTo(int.Parse(matchY.Groups[1].Value));
           if (compareNumber == 0)
               return compareNumber = matchX.Groups[2].Value.ToUpper().CompareTo(matchY.Groups[2].Value.ToUpper());
           else
               return compareNumber;
       }
   }

 

Exemple :

var numbers = new List<string> { "9", "10B", "243", "10a", "10" };
numbers.Sort(new StreetNumberComparer());

numbers : "9", "10", "10a", "10B", "243"

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

J'ai corrigé une erreur dans le code.

 

Suivant ce que tu as à faire, il peut être intéressant de définir une classe Adresse qui implémente IComparable avec des propriétés Rue (de type string) et Numero (d'un type StreetNumber qui implémente aussi IComparable comme ci-dessus).

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Ça m'amusait et il m'est arrivé d'avoir à utiliser des adresses postales (dans un programme de distribution de fibre optique).

 

Donc une petite classe qui définit un type Adresse (ici juste le numéro et le nom de la rue) et qui utilise un type NumeroDeRue (numéro avec éventuellement un suffixe numérique). Ces deux type sont "triables" (implémentation de IComparable et IComparable)

 

Adresse

using System;
using System.Text.RegularExpressions;

namespace Gile.AdressePostale
{
   /// <summary>
   /// Décit une adresse postale interne à une ville (numéro et nom de rue).
   /// </summary>
   public class Adresse : IComparable, IComparable<Adresse>
   {
       string street, text;
       NumeroDeRue number;

       /// <summary>
       /// Obtient le numéro.
       /// </summary>
       public NumeroDeRue Numero => number;

       /// <summary>
       /// Obtient le nom de la rue.
       /// </summary>
       public string Rue => street;

       /// <summary>
       /// Crée une nouvelle instance de Adresse.
       /// </summary>
       /// <param name="number">Numéro (avec éventuellement un suffixe alphabétique).</param>
       /// <param name="street">Nom de la rue.</param>
       public Adresse(string number, string street)
       {
           try { this.number = new NumeroDeRue(number); }
           catch { throw; }
           this.street = street ?? throw new ArgumentNullException("street");
           text = number + " " + street;
       }

       /// <summary>
       /// Crée une nouvelle instance de Adresse.
       /// </summary>
       /// <param name="address">Adresse (numéro rue).</param>
       public Adresse(string address)
       {
           if (address == null)
               throw new ArgumentNullException("adress");
           var regex = new Regex(@"(?i)^(?<number>\d+[A-Z]*)\s+(?<street>.+)$");
           var match = regex.Match(address);
           if (!match.Success)
               throw new FormatException($"Format d'adresse non valide {address}");
           text = address;
           number = new NumeroDeRue(match.Groups["number"].Value);
           street = match.Groups["street"].Value;
       }

       /// <summary>
       /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique 
       /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri.
       /// </summary>
       /// <param name="obj">Objet à comparer à cette instance.</param>
       /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns>
       public int CompareTo(object obj)
       {
           if (obj != null && !(obj is Adresse))
               throw new ArgumentException("L'objet n'est pas de type Adresse");
           return CompareTo((Adresse)obj);
       }

       /// <summary>
       /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique 
       /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri.
       /// </summary>
       /// <param name="other">Instance de Adresse à comparer à cette instance.</param>
       /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns>
       public int CompareTo(Adresse other)
       {
           if (other == null) return 1;
           int compareStreet = string.Compare(street, other.street, true);
           return compareStreet == 0 ? number.CompareTo(other.number) : compareStreet;
       }

       /// <summary>
       /// Détermine si l'objet spécifié est identique à l'objet actuel.
       /// </summary>
       /// <param name="obj">Objet à comparer à l'objet actuel.</param>
       /// <returns>true si l'objet spécifié est égal à l'objet actuel ; sinon, false.</returns>
       public override bool Equals(object obj) =>
           obj is Adresse && text.ToUpper().Equals(((Adresse)obj).text.ToUpper());

       /// <summary>
       /// Fait office de fonction de hachage par défaut.
       /// </summary>
       /// <returns>Code de hachage pour l'objet actuel.</returns>
       public override int GetHashCode() => text.ToUpper().GetHashCode();

       /// <summary>
       /// Retourne une chaîne qui représente l'objet actuel.
       /// </summary>
       /// <returns>Chaîne qui représente l'objet actuel.</returns>
       public override string ToString() => text;
   }
}

 

NumeroDeRue

using System;
using System.Text.RegularExpressions;

namespace Gile.AdressePostale
{
   /// <summary>
   /// Décrit un numéro de rue contenant éventuellement un suffixe alphabétique.
   /// </summary>
   public class NumeroDeRue : IComparable, IComparable<NumeroDeRue>
   {
       int number;
       string suffix, text;

       /// <summary>
       /// Obtient le numéro.
       /// </summary>
       public int Numero => number;

       /// <summary>
       /// Obtient le suffixe
       /// </summary>
       public string Suffixe => suffix;

       /// <summary>
       /// Crée une nouvelle instance de NumeroDeRue.
       /// </summary>
       /// <param name="streetNumber">Numéro complet.</param>
       public NumeroDeRue(string streetNumber)
       {
           text = streetNumber ?? throw new ArgumentNullException("streetNumber");
           var regex = new Regex(@"(?i)^(\d+)([A-Z]*)$");
           var match = regex.Match(text);
           if (!match.Success)
               throw new FormatException($"Format de numéro de rue non valide : {streetNumber}");
           number = int.Parse(match.Groups[1].Value);
           suffix = match.Groups[2].Value;
       }

       /// <summary>
       /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique 
       /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri.
       /// </summary>
       /// <param name="obj">Objet à comparer à cette instance.</param>
       /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns>
       public int CompareTo(object obj)
       {
           if (obj != null && !(obj is NumeroDeRue))
               throw new ArgumentException("L'objet n'est pas de type NumeroDeRue");
           return CompareTo((NumeroDeRue)obj);
       }

       /// <summary>
       /// Compare l'instance actuelle avec un autre objet du même type et retourne un entier qui indique 
       /// si l'instance actuelle précède ou suit un autre objet ou se trouve à la même position dans l'ordre de tri.
       /// </summary>
       /// <param name="other">Instance de NumeroDeRue à comparer à cette instance.</param>
       /// <returns>Valeur qui indique l'ordre relatif des objets comparés.</returns>
       public int CompareTo(NumeroDeRue other)
       {
           if (other == null) return 1;
           var compareNumber = number.CompareTo(other.number);
           return compareNumber == 0 ? string.Compare(suffix, other.suffix, true) : compareNumber;
       }

       /// <summary>
       /// Détermine si l'objet spécifié est identique à l'objet actuel.
       /// </summary>
       /// <param name="obj">Objet à comparer à l'objet actuel.</param>
       /// <returns>true si l'objet spécifié est égal à l'objet actuel ; sinon, false.</returns>
       public override bool Equals(object obj) =>
           obj is NumeroDeRue && text.ToUpper().Equals(((NumeroDeRue)obj).text.ToUpper());

       /// <summary>
       /// Fait office de fonction de hachage par défaut.
       /// </summary>
       /// <returns>Code de hachage pour l'objet actuel.</returns>
       public override int GetHashCode() => text.ToUpper().GetHashCode();

       /// <summary>
       /// Retourne une chaîne qui représente l'objet actuel.
       /// </summary>
       /// <returns>Chaîne qui représente l'objet actuel.</returns>
       public override string ToString() => text;
   }
}

 

Un exemple d'utilisation :

using Gile.AdressePostale;
using System;
using System.Linq;

namespace ConsoleApp
{
   class Program
   {
       static void Main(string[] args)
       {
           string[] adresses = new[]
           {
               "8 avenue John McCarthy",
               "5b rue du Code",
               "14 avenue John McCarthy",
               "5a rue du Code",
               "5 rue du Code",
           };
           foreach (var item in adresses.OrderBy(a => new Adresse(a)))
           {
               Console.WriteLine(item);
           }
           Console.WriteLine();
           foreach (var item in adresses.Select(a => new Adresse(a)).GroupBy(a => a.Rue))
           {
               Console.WriteLine($"{item.Key} : {item.Count()} adresses");
           }
       }
   }
}

Résultat :

8 avenue John McCarthy
14 avenue John McCarthy
5 rue du Code
5a rue du Code
5b rue du Code

avenue John McCarthy : 2 adresses
rue du Code : 3 adresses

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Ayant été Marseillais durant 3 ans, je préfèrerais redescendre dans le sud.

Je ne suis pas sûr que Gilles ait envie de monter voir les "parisiens", même si le sud Essonne est très sympa pour se balader et découvrir pas mal de petits bijoux (forestiers et architecturaux).smile.gif

 

Sinon, je n'ai malheureusement pas le niveau pour écrire ton code, mais je vais potasser ça pour essayer de tout comprendre avant de l'appliquer.

 

Bon allez week-end oblige, je m'y remets demain!

 

Olivier

Lien vers le commentaire
Partager sur d’autres sites

Une version commentée de la classe StreetNumberComparer.

 

    class StreetNumberComparer : IComparer<string>
   {
       public int Compare(string x, string y)
       {
           //var regex = new Regex(@"(?i)^([0-9]+)([A-Z]*)$");
           var regex = new Regex(@"
               (?i)       # ignorer la casse
               ^          # début de la chaîne
               ([0-9]+)   # un ou plusieurs chiffres (groupe 1)
               ([A-Z]*)   # zéro ou plusieurs lettres (groupe 2)
               $          # fin de la chaîne
               ", RegexOptions.IgnorePatternWhitespace); // option pour modèle avec des commentaires

           // évalue si la chaîne x correspond au modèle et constitue les groupes
           var matchX = regex.Match(x);
           if (!matchX.Success)
               throw new FormatException($"Numéro de rue non valide : {x}");

           // évalue si la chaîne y correspond au modèle et constitue les groupes
           var matchY = regex.Match(y);
           if (!matchY.Success)
               throw new FormatException($"Numéro de rue non valide : {y}");

           // compare les valeurs numériques des deux numéros (groupe 1)
           var compareNumber = int.Parse(matchX.Groups[1].Value).CompareTo(int.Parse(matchY.Groups[1].Value));
           if (compareNumber == 0) // si les numéros sont égaux
               // compare les deux suffixes des numéros (groupe 2)
               return compareNumber = matchX.Groups[2].Value.ToUpper().CompareTo(matchY.Groups[2].Value.ToUpper());
           else
               return compareNumber;
       }
   }

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Coucou

 

Y'a besoin d'un programme pour ça ?

Moi, quand j'arrive dans une rue, je me trompe pas, je regarde sur les maisons !!!

C'est pourtant simple...

 

Blague à part, je vois que (gile) excelle comme toujours, bien que je ne sache pas évaluer le programme

Ce que je vois c'est que tu as la fibre "développeur", pour le moins

C'est là qu'on voit la force : expliquer un souci, le résoudre

Moi il va falloir me l'expliquer plusieurs fois avant que je propose une solution

Grand respect !

Lien vers le commentaire
Partager sur d’autres sites

Salut,

 

Pourtant la problématique est simple : trier des chaînes de caractères figurant des numéros de rue ; soit des chaînes commençant par un ou plusieurs chiffres, éventuellement suivis de lettres.

Par exemple "12", "5", "5B", "5A".

On connait bien les problèmes posés par le tri alphabétique de chaînes figurant des nombres : "12" se retrouve avant "5". S'ajoute ici la question du suffixe.

 

Il faut donc trouver une moyen de trier d'abord suivant la valeur numérique (nombre entier) la partie numérique au début de la chaîne, puis, en cas d'égalité de cette valeur, de trier de manière alphabétique le reste de la chaîne.

 

Pour implémenter l'algorithme général ci-dessus, il faut trouver un moyen de séparer la partie numérique du début de la chaine du reste.

On pourrait écrire une fonction, mais, malgré l'adage*, j'ai pensé qu'utiliser les expressions régulières était un moyen tout à fait adapté.

 

Les expressions régulières constituent une technique aussi puissante qu'obscure pour traiter des chaînes de caractères. La classe .NET Regex utilise cette technique et permet à l'aide de modèles (patterns) de rechercher, modifier, récupérer des occurrences de chaînes dans une chaîne de caractère.

 

Le "côté obscur" des expressions régulières, c'est la définition du modèle à l'aide des nombreux caractères spéciaux.

 

Le modèle dans notre exemple est : @"(?i)^([0-9]+)([A-Z]*)$"

(?i) indique qu'on veut ignorer la casse (on aurait aussi pu utiliser l'option RegexOptions.IgnoreCase

^ indique que le caractère suivant doit se trouver en début de chaîne

([0-9]+) les parenthèses indiquent un groupe constitué ici d'un ou plusieurs caractère numériques

([A-Z]*) indique un autre groupe constitué de zéro ou plusieurs caractères alphabétiques (donc éventuellement une chaîne vide)

$ indique que le caractère précédent doit se trouver en fin de chaîne

 

Mais une fois le modèle élaboré, la méthode Regex.Match() permet à la fois de tester la validité du format de la chaîne (propriété Success) et, si le format est valide, de récupérer la partie numérique d'un côté et le suffixe de l'autre (propriété Groups)

 

On peut ensuite comparer ces deux bouts de chaîne

- soit dans l'implémentation de la méthode Compare() d'une classe implémentant IComparer qu'on poura utiliser comme argument avec List.Sort() ou avec IEnumerable.OrderBy() (CF réponse #4).

- soit dans les méthodes CompareTo() d'une classe définissant un objet directement triable en implémentant IComaprable et IComparable (il est préférable d'implémenter les deux la méthode CompareTo() de IComparable appelant celle de IComaprable) (CF réponse #7).

 

 

* à propos des expressions régulières, on entend parfois :

"Certaines personnes, confrontées à un problème, pensent : "Je vais utiliser les expressions régulières". Maintenant, elles ont deux problèmes."

Gilles Chanteau - gileCAD - GitHub
Développements sur mesure pour AutoCAD

Lien vers le commentaire
Partager sur d’autres sites

Coucou

 

Merci de ces explications toutefois je suis largué... et je m 'interroge sur l'équipement dont tu es doté pour naviguer à cette altitude...

 

obn6.jpg

 

Dans tous les cas un grand merci, il est bien possible qu'un jour on en ait besoin de ces explications.

 

Par exemple : je pense que si j'avais dû traiter ce sujet je serais passé par une transformation en ascii pour ensuite gérer des numéros, mais c'est une vague idée, pour dire que chacun voit la résolution du problème avec ses outils et ses connaissances.

 

Amicalement

Lien vers le commentaire
Partager sur d’autres sites

Salut à tous,

 

@Didier : je crois qu'il est pas sur cette planète, j'ai trouvé une carte mais c'est succins : http://stars.chromeexperiments.com/

"98% des soucis informatiques sont assis entre la chaise et le bureau !"

 

"C'est parce que la vitesse de la lumière est supérieure à celle du son que tant de gens paraissent brillants avant d'avoir l'air con."
Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité