6
votes

LINQ - Groupez une liste des paires basées sur une propriété

J'ai une liste d'objets avec une propriété pouvant être utilisée pour partitionner les objets en paires. Je sais à l'avance que chaque objet fait partie d'une paire.

Voici un exemple pour illustrer la situation: p>

p>


J'ai une liste de chaussures individuelles que j'aimerais regrouper en paires. P>

Disons que ma liste est la suivante: P>

var pairsByBrand = shoes.GroupBy(s => s.Brand);
foreach (var group in pairsByBrand)
{
    Console.WriteLine("Pair:");
    foreach (var shoe in group)
    {
        Console.WriteLine(shoe);
    }
    Console.WriteLine();
}


3 commentaires

Qu'est-ce qui empêche la paire {19,60}?


@AustinSalonen que la paire serait acceptable aussi. La seule restriction est que les paires doivent être de la même marque, à gauche et à une chaussure droite.


@Ryan Kohn Vous avez besoin d'une autre propriété, par exemple le produitName et chaque L et R, qui devrait être une paire, doit avoir le même nom de produit. Ensuite, groupe par produit non par marque, car par marque, vous avez 4 articles pour le groupe Nike et 2 pour le groupe Asics


3 Réponses :


3
votes
var shoesByBrand = shoes.GroupBy(s => s.Brand);
foreach (var byBrand in shoesByBrand)
{
    var lefts = byBrand.Where(s => s.LeftOrRight == LeftOrRight.L);
    var rights = byBrand.Where(s => s.LeftOrRight == LeftOrRight.R);
    var pairs = lefts.Zip(rights,(l, r) => new {Left = l, Right = r});

    foreach(var p in pairs)
    {
        Console.WriteLine("Pair:  {{{0}, {1}}}", p.Left.Id, p.Right.Id);
    }

    Console.WriteLine();
}
Note:  Zip will only pair up as much as it can.  If you have extra rights or lefts they won't get reported.

4 commentaires

+1 J'ai besoin de lire sur groupeby et zip contre, il a l'air fort :) BTW, avez-vous voulu dire Console.ReDkey (); À la fin à la fin?


Je si tout gauche et droit peut aller ensemble tant que la marque est la même. De l'OP: "Cette paire serait acceptable aussi. La seule restriction est que les paires doivent être de la même marque, avec une seule et une chaussure droite."


@Aquinas: Si vous commencez à répertorier toutes les paires possibles , la sortie explose avec relativement peu de chaussures. Il y avait une certaine ambiguïté dans la question mais votre réponse et votre mienne devraient le couvrir.


Alors que la réponse de Thom Smith était plus succincte, j'ai fini par utiliser celui-ci parce que le code était plus clair.



8
votes

Pure Linq fonctionnel, à l'aide de SelectMany code> et zip code>, cédant un ienumerable code> de tuple code> S:

IEnumerable<Tuple<Shoe, Shoe>> pairs = shoes
    .GroupBy(shoe => shoe.Brand)
    .SelectMany(brand=>
        Enumerable.Zip(
            brand.Where(shoe=>shoe.LeftOrRight == LeftOrRight.L),
            brand.Where(shoe=>shoe.LeftOrRight == LeftOrRight.R),
            Tuple.Create
        )
    );


3 commentaires

Belle solution, mais encore une fois, ne sont pas des paires (avec gauche et droite) qui sont la même marque capable d'être regroupées? Avec cette solution, vous obtiendrez différentes appariements en fonction de la commande que les chaussures apparaissent dans la liste. Donc, si j'ajoute des chaussures 11, puis 60, ils formeront une paire, mais si j'ajoute 11 et 29 d'abord, ils formeraient une paire. Est-ce ce qui est attendu? Nous avons besoin de l'OP pour peser.


@aquinas: L'OP a déjà clarifié que deux chaussures de gauche de la même marque sont identiques aux fins de la présente discussion.


Mon interprétation était que chaque chaussure doit être présente exactement une fois dans la sortie et que l'ordre n'était pas pertinent tant que chaque paire était valide. L'OP peut avoir été un peu vague sur ce point.



2
votes

Un moyen de le faire:

var pairs = shoes.GroupBy(s => s.Brand)
                 .Select(g => g.GroupBy(s => s.LeftOrRight));
                 .SelectMany(Enumerable.Zip(g => g.First(), g => g.Last(),Tuple.Create));


0 commentaires