2010-02-14 4 views
7

Supposons que j'ai une liste d'éléments (par exemple, Posts) et que je souhaite trouver le premier élément en fonction d'un ordre non trivial (par exemple, PublishDate et ensuite CommentsCount comme un tie-breaker). La façon naturelle de le faire avec LINQ est comme ceci:Comment trouver le premier article selon un ordre spécifique en utilisant LINQ dans O (n)?

posts.OrderBy(post => post.PublishDate).ThenBy(post => post.CommentsCount).First() 

Cependant, le micro-optimiseur me craint que l'appel OrderBy me coûte réellement O (n * LGN) pour trier la liste entière, quand tout J'ai vraiment besoin d'une opération O (n) find-minimum. Donc, LINQ est-il assez intelligent pour renvoyer quelque chose de OrderBy() qui sait comment optimiser les appels First() suivants? Si ce n'est pas le cas, quelle est la meilleure façon de le faire dès la sortie de l'emballage? (Je peux toujours écrire ma propre implémentation de FindMinimumItem mais cela semble être trop compliqué).

+0

si la clé est réellement imprimé vous n'avez pas besoin de ThenBy mais pourrait plutôt créer une clé Coumpund des deux. Ce qui serait facile puisque le premier est soit un long (tick) ou un fixe avec string. et ce serait en fait être O (n) que vous demandez mais là encore il n'y a aucune garantie que O (n) est plus rapide que O (nlogn) –

Répondre

2

Le tri est intelligent dans la façon dont il ne le fera que le ThenBy sur le premier groupe de la OrderBy, mais le OrderBy doit encore trier tous les éléments avant de pouvoir renvoyer le premier groupe.

Vous pouvez utiliser la méthode globale pour obtenir le premier poste selon une comparaison personnalisée: (. En supposant que vous utilisez LINQ to Objects bien sûr)

Post lowest = 
    posts.Aggregate((Post)null, 
    (x, y) => 
     x == null 
     || y.PublishDate < x.PublishDate 
     || (y.PublishDate == x.PublishDate && y.CommentsCount < x.CommentsCount) 
     ? y : x 
); 

+0

Merci, c'est une bonne solution mais un peu bavarde. – Avish

0

Habituellement c'est un max ou min (je ne sais pas comment ça s'appelle dans LinQ), donné une clé spécifique; trier et obtenir le premier ou le dernier semble exagéré dans n'importe quel langage ou cadre.

+0

Les méthodes qui prennent des clés dans LINQ retournent malheureusement la clé maximum/minimum elle-même plutôt que l'objet contenant cette clé. –

+0

ne pouvez-vous pas faire une clé composée alors? En python, il est très typique d'organiser un tuple temporaire qui contient la clé et l'ensemble du dossier, puisqu'ils sont comparés lexicographiquement. – fortran

+1

Bien que .NET 4.0 ait un type Tuple, .NET 3.5 ne l'est pas. Il est possible que cette approche fonctionne dans .NET 4.0, mais je ne suis pas sûr que ce soit très idiomatique. –

2

Est-ce dans SQL ou LINQ to Objects? Si c'est le dernier, vous voulez probablement MinBy de MoreLINQ; votre déclaration telle qu'écrite va en effet trier et ensuite prendre le premier article.

Et oui, il est dommage qu'il ne comprend pas cela (et des choses similaires comme DistinctBy) sortie de l'emballage.

EDIT: Je vois que votre question a maintenant changé; MoreLINQ ne supporte pas une comparaison composée comme ça. En MiscUtil j'ai le code pour créer un composé IComparer<T> - vous pouvez passer cela en MinBy en utilisant la fonction d'identité comme le sélecteur de clé. Vous pouvez ajouter une demande de fonctionnalité pour une MinBy qui prend une source et un IComparer<T> sans sélecteur à clé :)

+0

Merci, je vais jeter un oeil à ceux-ci. Il est vraiment dommage qu'il n'y ait pas de moyen original de convertir entre des ensembles d'expressions clés (par exemple, comme utilisé avec OrderBy ... ThenBy) et un IComparer. – Avish

+1

Une alternative à 'ThenBy' pourrait renvoyer un tuple:' Tuple.Create (post.PublishData, post.CommentsCount) '. –

Questions connexes