Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

Comme Mitsu :-)

Vu que j'ai un peu pitié de Simon qui n'a toujours pas eu le temps de répondre à un quizz de Mitsu Stick out tongue, j'ai décidé de proposer moi-même des quizz.

Donc voici le premier. L'idée est de partir d'un IEnumerable<IEnumerable<T>> afin de récupérer tous les éléments distincts présents dans tous les sous-ensembles. Mais attention, il faut prévoir tous les cas :

static void Main(string[] args)

{

    Console.WriteLine("Test 1");

    foreach (int i in GetElementsInAllGroups(new int[][] { new int[] { 1, 2, 3, 3 }, new int[] { 2, 2, 3, 4 } }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 2");

    foreach (int i in GetElementsInAllGroups(new int[][] { new int[] { 1, 2, 3, 3 }, new int[] { 2, 2, 3, 4 }, new int[]{1, 5, 6} }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 3");

    foreach (int i in GetElementsInAllGroups(new int[][] { new int[0] }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 4");

    foreach (int i in GetElementsInAllGroups(new int[][] { new int[] { 1, 2, 3, 3 }, new int[0] }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 5");

    foreach (int i in GetElementsInAllGroups(new int[][] { new int[] { 1, 2, 3, 3 }, null }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 6");

    foreach (int i in GetElementsInAllGroups(new int[][] { null, new int[] { 1, 2, 3, 3 } }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 7");

    foreach (int i in GetElementsInAllGroups(new int[][] { null }))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 8");

    foreach (int i in GetElementsInAllGroups(new int[0][]))

        Console.WriteLine("\t{0}", i);

 

    Console.WriteLine("Test 9");

    foreach (int i in GetElementsInAllGroups<int>(null))

        Console.WriteLine("\t{0}", i);

}

 

public static IEnumerable<T> GetElementsInAllGroups<T>(IEnumerable<IEnumerable<T>> values)

{

    // Que mettre ici !!!

}

Le résultat dans la console doit être :

Test 1
        2
        3
Test 2
Test 3
Test 4
Test 5
Test 6
Test 7
Test 8
Test 9

Enjoy Smile

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié vendredi 8 août 2008 13:10 par Matthieu MEZIL

Classé sous : , , ,

Commentaires

# re: Comme Mitsu :-) @ vendredi 8 août 2008 15:30

Pourquoi 2 et 3 ?

Ce sont les seuls éléments présents à la fois dans { 1, 2, 3, 3 } et dans { 2, 2, 3, 4 }

Matthieu MEZIL

# re: Comme Mitsu :-) @ vendredi 8 août 2008 17:36

Petite précision, j'attend une réponse en C#3, pas en C#2.

Et allez, ne soyons pas extrêmiste, si vous préférez, vous pouvez aussi le faire en VB .NET 2008.

Matthieu MEZIL

# re: Comme Mitsu :-) @ samedi 9 août 2008 01:07

if (values != null)

{

   var r = values.Where(v => v != null)

                 .SelectMany(v => v.Distinct())

                 .GroupBy(v => v)

                 .Where(g => g.Count() == values.Count())

                 .Select(g => g.Key);

   foreach (T t in r)

   {

       yield return t;

   }

}

Sacha

# re: Comme Mitsu :-) @ samedi 9 août 2008 01:30

Ca marche effectivement mais je pensais à une réponse plus élégante et plus performante.

Personne d'autre ?

Matthieu MEZIL

# re: Comme Mitsu :-) @ samedi 9 août 2008 11:54

Il suffit d'accumuler les intersections.

Matthieu, quelle était ta solution pour voir ?

private static IEnumerable<T> GetElementsInAllGroups<T>(IEnumerable<IEnumerable<T>> values)

{

   var empty = new T[0];

   if (values == null) return empty;

   var temp = values.ToArray();

   return temp.Length > 0 ? temp.Aggregate((acc, v) => acc.Intersect(v)) ?? empty : empty;

}

La lisibilité en prend un coup mais j'aime bien :)

Mitsu

# re: Comme Mitsu :-) @ samedi 9 août 2008 12:14

Je pensais à ça :

public static IEnumerable<T> GetElementsInAllGroups<T>(IEnumerable<IEnumerable<T>> values)

{

    IEnumerable<T> valueFirst;

    if (values != null && values.Any() && (valueFirst = values.First()) != null)

        return from v in valueFirst.Distinct()

               where values.Skip(1).All(v2 => v2!= null && v2.Any(v3 => v3.Equals(v)))

               select v)

               yield return value;

}

J'aime pas trop le ToArray mais j'aime beaucoup le coup du empty que je trouve très astucieux. Du coup ça propose ça :

public static IEnumerable<T> GetElementsInAllGroups<T>(IEnumerable<IEnumerable<T>> values)

{

    IEnumerable<T> valueFirst;

    if (values != null && values.Any() && (valueFirst = values.First()) != null)

        return from v in valueFirst.Distinct()

               where values.Skip(1).All(v2 => v2 != null && v2.Any(v3 => v3.Equals(v)))

               select v;

    return new T[0];

}

Matthieu MEZIL

# re: Comme Mitsu :-) @ samedi 9 août 2008 12:41

Hé Mitsu, ta méthode elle marche même pas Stick out tongue

Elle passe pas le test 5 !

Et pour le cas suivant,

GetElementsInAllGroups(new int[][] { new int[] { 1, 2, 3, 3 } })

elle retourne 1, 2, 3, 3 au lieu de 1, 2, 3.

Matthieu MEZIL

# re: Comme Mitsu :-) @ samedi 9 août 2008 13:35

1- ma méthode marche. Ton énoncé n'est pas assez clair. Il ne précise pas si le distinct porte sur chacune des énumérations ou sur l'ensemble recherché.

2- elle est beaucoup plus Linq que la tienne :p : Aggregate + Intersect sans redescendre au niveau de l'itérateur, c'est quand même autre chose que de faire une boucle avec un where et des skips :)

J'aurais pu me passer du Array mais il est là juste pour la performance.

Mitsu

# re: Comme Mitsu :-) @ samedi 9 août 2008 14:13

@ Mitsu:

1- ok l'énoncé est pas clair mais sur le test 5, ta méthode génère une exception car Intersect(null)

2 - c vrai que ta méthode Aggregate + Intersect c'est classe! Pour ce qui est de la boucle, c'est pour ça que je préfère la deuxième solution où j'utilise le new T[0]. Moins LINQ que la tienne, elle est quand même pas mal non plus : Any, First, Distinct, Skip, All, je trouve ça pas si mal.

Par contre c'est vrai que, contrairement à ma première impression, c'est intéressant le fait de d'abord passer par un tableau pour la perf. Allez, je te l'accorde, ça reste toi le boss. Wink

Matthieu MEZIL

# re: Comme Mitsu :-) @ dimanche 10 août 2008 21:22

Simon, il est en vacance, et y'a même pas la 3G (la je suis sur un smartphone en EDGE).

A premiere vue :

var enumCount = enums.Count();

return enums

       .Where(en=>en != null)

       .SelectMany(

          en=> en.Distinct())

       .GroupBy(val=>val)

       .Where(gp=>gp.Count() == enumCount)

       .Select(gp=> gp.Key);

j'ai pas de compilo sous la main donc a tester ^^

simon ferquel

# re: Comme Mitsu :-) @ lundi 11 août 2008 00:59

N'étant pas un adepte du code "le plus illisible en le moins de lignes possible" :-), je livre ma version, qui utilise de Linq ici juste ce qui est intéressant à mon avis.

public static IEnumerable<T> GetElementsInAllGroups<T>(IEnumerable<IEnumerable><T>> values)

{

 if (values == null) return Enumerable.Empty<T>();

IEnumerable<T> x = null;

foreach (IEnumerable<T> e in values)

{

 if (e == null) return Enumerable.Empty<T>();

 x = (x == null) ? e : e.Intersect<T>(x);

}

return (x != null) ? x : Enumerable.Empty<T>();

}

Je suis sûr qu'on doit pouvoir exprimer cela avec des expression Linq au lieu de méthodes, mais comme je n'y comprends rien...

PS: allouer un Array pour une collection de taille à priori inconnue? allons Mitsu, c'est quand même l'intéret No1 du yield, de LINQ, etc... de ne **rien** allouer :)

Simon Mourier (Un autre simon!)

smo

# re: Comme Mitsu :-) @ lundi 11 août 2008 08:34

@ Simon F: pas d'Internet ! Dur dur ! Ta solution marche à l'exception du test 9 (c juste un if à rajouter). C'est celle proposée plus haut par Sacha.

@ Simon M: il te manque un petit Distinct mais ça marche. En plus, je ne connaissais pas le Enumerable.Empty(). Bon en fait il ne fait que retourner un tableau vide en Singleton mais c'est cool quand même, tu m'as fait découvrir une méthode. Pour ta remarque sur le ToArray de Mitsu, c'est pas faux par contre, je ne suis pas d'accord avec toi pour ce qui est de dire que c'est l'intérêt n°1 du yield. Pour moi l'intérêt n°1 est de renvoyer les valeurs au fur et à mesure (sans toutes les récupérer) et de garder la position courante.

Matthieu MEZIL

# re: Comme Mitsu :-) @ lundi 11 août 2008 08:54

hem... ben que vois tu comme différence "renvoyer les valeurs au fur et à mesure (sans toutes les récupérer) et de garder la position courante" et "ne pas allouer de stockage intermédiaire"? le yield c'est le streaming objet.

PS1: La différence entre Enumerable.Empty() et new T[0] (outre le fait que c'est exactement fait pour cela), c'est qu'il n'y a pas d'allocation car le résultat est caché.

PS2: pour le distinct, j'avais renvoyé au départ x.Distinct<T>() mais je n'ai pas réussi à trouver de cas qui en ai besoin. Bon j'ai pas beaucoup cherché, faut dire :)

PS3: j'aimerais bien que quelqu'un me donne l'équivalent avec une VRAIE syntaxe expression (à la SQL, select from etc..., pas machin.truc<T>), ça m'aidera peut-être à comprendre l'intêret de ce truc. Mitsu si tu me lis :)

smo

# re: Comme Mitsu :-) @ lundi 11 août 2008 10:13

D'ailleurs il est intéressant de noter que, d'après de rapides tests, ma solution va ~5 fois plus vite que la tienne (y compris avec un distinct). Ahhh les voies de Linq sont impénétraaaables...

smo

# re: Comme Mitsu :-) @ lundi 11 août 2008 11:47

@Simon M: "ben que vois tu comme différence "renvoyer les valeurs au fur et à mesure (sans toutes les récupérer) et de garder la position courante" et "ne pas allouer de stockage intermédiaire"? "

Je veux dire qu'il y a certes l'idée du stockage qui est très importante mais aussi l'idée de calcul. Imagine le code suivant :

public IEnumerable<int> Test(IEnumerable<int> values)

{

 foreach (int v in value)

    yield return MaFonctionMathematiqueTresCompliquee(v);

}

et l'appelant qui fait ça :

foreach (int v in Test(values))

 if (v > 100)

    break;

Dans ce cas, on n'aura pas eu à exécuter MaFonctionMathematiqueTresCompliquee sur l'ensemble des valeurs du tableau dans le cas où une valeur est supérieure à 100.

PS1: Pour moi l'intérêt il est que si tu appelles 10 méthodes succeptibles de te retourner un IEnumerable<T> vide (ou 10 fois la même méthode), il n'y a qu'une seule affectation de T[0] qui ets faite car c'est un singleton.

PS2: GetElementsInAllGroups1(new int[][] { new int[] { 1, 2, 3, 3 } })

PS3: Pour ce qui est de la requête LINQ, on ne peut pas tout faire avec LINQ. Par ex, tu ne peux pas faire ça :  

if (e == null) return Enumerable.Empty<T>();

Oui je suis bien conscient que ma méthode n'est pas optimale loin de là mais ce n'est pas le but de ce quizz. Une première raison pour expliquer ça est le fait que d'appeler plusieurs méthodes successives est coûteux. L'autre raison c'est que mon algo d'intersection est certes sympa (du moins je trouve) mais pas franchement optimal.

Matthieu MEZIL

# re: Comme Mitsu :-) @ lundi 11 août 2008 13:37

1ere ligne : if(enums == null) throw new ArgumentNullException("enums must not be null");

simon ferquel

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- « Naviguer vers le haut » dans une librairie SharePoint par Blog de Jérémy Jeanson le 10-07-2014, 13:21

- PowerShell: Comment mixer NAGIOS et PowerShell pour le monitoring applicatif par Blog Technique de Romelard Fabrice le 10-07-2014, 11:43

- ReBUILD 2014 : les présentations par Le blog de Patrick [MVP Office 365] le 10-06-2014, 09:15

- II6 Management Compatibility présente dans Windows Server Technical Preview avec IIS8 par Blog de Jérémy Jeanson le 10-05-2014, 17:37

- Soft Restart sur Windows Server Technical Preview par Blog de Jérémy Jeanson le 10-03-2014, 19:43

- Non, le certificat public du CA n’est pas un certificat client !!! par Blog de Jérémy Jeanson le 10-03-2014, 00:08

- Windows Server Technical Preview disponible via MSDN par Blog de Jérémy Jeanson le 10-02-2014, 19:05

- Focus Sauvegardes SharePoint par Le blog de Patrick [MVP Office 365] le 10-02-2014, 13:11

- Technofolies, votre évènement numérique de l'année par Le Blog (Vert) d'Arnaud JUND le 09-26-2014, 18:40

- Xamarin : From Zero to Hero par Fathi Bellahcene le 09-24-2014, 17:35