7
votes

Index LINQ-TO-Objets dans un groupe + pour différents groupements (AKA Row_Number avec partition par équivalent)

Après beaucoup de recherche Google et expérimentation de code, je suis souples sur un problème complexe C # Linq-Objects qui dans SQL serait facile à résoudre avec une paire de rangées de rangée () ... Partition par des fonctions et une sous-requête ou deux.

Voici, en mots, ce que j'essaie de faire en code - l'exigence sous-jacente élimine les documents en double à partir d'une liste:

  1. Premièrement, groupe une liste de (document.title, document.sourceid), en supposant une définition de classe (simplifiée) comme ceci:
    XXX
  2. Dans ce groupe, attribuez chaque document un index (E.G. Index 0 == 1er document avec ce titre à partir de cette source, index 1 = document 2e avec ce titre de cette source, etc.). J'adorerais l'équivalent de Row_Number () dans SQL!


  3. Maintenant, groupe par (document.title, index), où l'index a été calculé à l'étape 2. Pour chaque groupe, ne renvoyez qu'un seul document: celui avec le document le plus bas.Sourceid.

    STEP # 1 est facile (E.G. codePonet.blogspot.com/2009/01/group-by-in-linq.html), mais je me suis englé sur les marches n ° 2 et n ° 3. Je ne peux pas sembler construire une requête de linq en C # sans squiggle de rouge pour résoudre les trois étapes.

    Anders Heilsberg's Post sur Ce fil est que je pense que la réponse aux étapes n ° 2 et n ° 3 ci-dessus si je pouvais obtenir la syntaxe droite.

    Je préférerais éviter d'utiliser une variable locale externe pour effectuer le calcul de l'index, comme recommandé sur slodge.blogspot.com/2009/01/adding-row-number-utilisation-linq-to-objects .html, puisque cette solution se casse si la variable externe est modifiée.

    de manière optimale, l'étape du groupe par titre pourrait être effectuée en premier, de sorte que les groupements "internes" (première par source pour calculer l'index, puis par index pour filtrer les doublons) peuvent fonctionner sur de petits nombres de Objets de chaque groupe "par titre", car le nombre de documents de chaque groupe de titres est généralement de moins de 100 ans. Je ne veux vraiment pas de solution N 2 !

    Je pourrais certainement résoudre celui-ci avec des boucles de foresach imbriquées, mais cela semble être le genre de problème qui devrait être simple avec Linq.

    Des idées?


0 commentaires

4 Réponses :


3
votes

Pour être honnête, je suis assez confus avec votre question. Peut-être que si vous devriez expliquer ce que vous essayez de résoudre. Quoi qu'il en soit, je vais essayer de répondre à ce que j'ai compris.

1) Tout d'abord, je suppose que vous avez déjà une liste de documents regroupés par titre code> + sourceId code> . À des fins de test, j'ai une liste de code d'accès à la liste suivante: p> xxx pré>

2) pour obtenir un index dans chaque élément, vous pouvez utiliser l'extension SELECT CODE> méthode, passant une fonction de sélecteur de func. Comme ceci: p> xxx pré>

3) de ce que j'ai compris, la prochaine étape consisterait à regrouper le dernier résultat par titre code>. Voici comment le faire: P>

foreach (var a in selectedFew) Console.WriteLine(a);
//The result will be:
//{ Doc = { Title = ABC, SourceId = 0 }, Index = 0 }
//{ Doc = { Title = 123, SourceId = 5 }, Index = 4 }


1 commentaires

Bonjour Jpochi - La solution de Dahlby était une bonne. Désolé, je n'ai pas été capable de vous recontacter, c'était ma première question sur le débordement de la pile et je ne m'attendais jamais à obtenir 2 réponses en moins de 2 heures le dimanche! La prochaine fois, je vais vérifier plus vite! :-) Quoi qu'il en soit, merci pour l'aide.



6
votes

Je pense que JPBOCHI a raté que vous souhaitez que vos groupements soient par paires de valeurs (titre + sourceID, puis index de titre +). Voici une solution LINQ (principalement) SOLUTION:

var selectedFew =
    from doc in docs
    group doc by doc.Title into titleGroup
    from docWithIndex in
        (
            from doc in titleGroup
            group doc by doc.SourceId into idGroup
            from docIndex in idGroup.Select((d, i) => new { Doc = d, Index = i })
            group docIndex by docIndex.Index into indexGroup
            select indexGroup.Aggregate((a,b) => (a.Doc.SourceId <= b.Doc.SourceId) ? a : b)
        )
    select docWithIndex;


1 commentaires

Hey Dahlbyk - C'est génial! Votre solution a l'air bien. Maintenant, je ne me sens pas si mal que d'être incapable de comprendre moi-même la première fois. J'ai découvert la surcharge Select-with-index mais ne pouvait pas comprendre comment l'obtenir dans une requête LINQ. Un code de ceinture en noir sur votre fin, merci pour l'aide et l'éducation dans ce qui est possible.



1
votes

Syntaxe basée sur la méthode: xxx

Diriez-vous que ce qui précède est la syntaxe méthode équivalente?


1 commentaires

Oui, cela émet le même résultat (correct) que la syntaxe Linq-y de Dahlbyk ci-dessus. Bien que (voir la requête mise à jour de Dahlby), il est probablement plus efficace de grouper par le titre d'abord afin que tout tri / agrégation peut se produire sur des ensembles minuscules-- S'il y avait un milliard de documents, cela ferait une grande différence car vous ne seriez pas à charger tout d'entre eux dans la RAM à la fois. De plus, la plupart des titres n'auront pas du tout des doublons ... J'espère que le BCL a optimisé le tri et les opérations group-par des ensembles d'un membre. :-)



1
votes

J'ai mis en œuvre une méthode d'extension. Il prend en charge plusieurs partitions par des champs ainsi que des conditions de commande multiples.

{ Title = "Title1", SourceId = 1 },
{ Title = "Title2", SourceId = 14 },
{ Title = "Title3", SourceId = 100 }


0 commentaires