2011-05-23 3 views
1

(exemple très simplifié) J'ai une liste générique de chaînes:Quelle est la manière la plus efficace et la plus lisible de diviser une liste générique en fonction d'une condition?

var strings = new List<string> { "abc", "owla", "paula", "lala", "hop" }; 

Je suis à la recherche de la façon la plus efficace de diviser cette liste en une liste des éléments qui répondent à une condition et une liste d'éléments qui ne répondent pas à cette même condition. Y a-t-il un moyen de faire cela avec une seule boucle dans la liste d'origine?

+1

La méthode d'extension 'Where' élimine un' IEnumerable 'mais le' RemoveAll' est une opération sur place. En tant que tel, vous comparez des opérations fondamentalement différentes. Qu'est-ce que "l'efficacité de l'écriture"? Concis, laconique, facile à comprendre? Vous ne le faites pas clairement. – spender

+1

Faites très attention en modifiant une liste sur laquelle vous avez une requête non matérialisée. La nature différée de LINQ rend cela déconseillé (bien que votre exemple * devrait * être correct) – spender

+0

Qu'est-ce qui est concis? Qu'est-ce qui est laconique? :) (Je ne suis pas l'anglais natif) Je veux dire facile à lire/comprendre. Mon code est-il correct maintenant? – TweeZz

Répondre

1

Cela le ferait:

IEnumerable<T> FilterAndRemove(this List<T> list, Func<T, bool> pred) 
{ 
    List<T> filtered = new List<T>(); 
    int i = 0; 
    while(i < list.Count) 
    { 
    if (pred(list[i])) 
    { 
     filtered.Add(list[i]); 
     list.RemoveAt(i); 
    } 
    else 
    { 
     ++i; 
    } 
    } 
    return list; 
} 

, mais je suis sûr que vous avez déjà pensé à quelque chose de similaire. Pouvez-vous s'il vous plaît mettre à jour votre réponse avec le type d'efficacité que vous recherchez? Notez que deux filtrages avec pred et !pred sur la liste d'origine seraient toujours O (n) et pas du tout inefficaces. Surtout en considérant que vous bénéficieriez pleinement de l'évaluation paresseuse pour les deux ensembles de résultats. Voir aussi la réponse de Rob.

Cet algorithme est dans O (n^2). Au lieu de supprimer chaque élément, vous pouvez également les rassembler dans une nouvelle liste et les copier dans la liste d'entrée avant de les renvoyer. Cela vous donnera aussi O (n).

Une option de plus pour O (n) serait de passer à une liste chaînée.

+0

Question mise à jour – TweeZz

+0

Je suis tenté d'y aller pour celui-ci .. Est-il correct de ne faire que boucler la liste originale une fois? – TweeZz

+0

@TweeZz Oui, il ne boucle qu'une fois, mais notez le 'RemoveAt (i)' qui heurte cet algorithme à O (n^2). J'ai posté cette réponse parce qu'elle fait exactement ce que votre code d'exemple fait, ce qui signifie qu'il mute la liste. Personnellement, je le ferais comme dans les réponses du compilateur JIM de Rob ou Snowbear. Quel est le cas d'utilisation de votre code et pourquoi les performances sont-elles critiques? – TheFogger

2

utiliser certains LINQ:

var matches = list.Select(s => s.IndexOf("o") > -1).ToList(); 
var notMatches = list.Except(matches).ToList(); 
list.Clear(); 
list.AddRange(matches); 

Mise à jour: comme cela a été mentionné dans les commentaires, faites attention muter la liste que les méthodes de LINQ essayer d'être à la demande, ils n'itérer la liste jusqu'à ce que vous commencer à regarder dans le IEnumerable. Cependant, dans mon cas, j'appelle le ToList, ce qui l'oblige à parcourir la liste complète des éléments.

+0

Select retourne juste une liste, non? Modifie-t-il également l'instance de la liste d'origine? Je veux dire que je dois finir avec 2 listes. Cela me donnerait une liste d'éléments correspondant à la condition et la liste d'origine, non? – TweeZz

+0

Sélectionnez * ne renvoie pas * une liste.Il renvoie un 'IEnumerable ' – spender

+0

Non, retourne juste un IEnumerable. Avez-vous besoin de muter l'original? –

1

Pourquoi ne pas utiliser

var stringsThatMeetCondition = strings.Where(condition); 
var stringsThatDontMeetCondition = strings.Where(x => !condition(x)); 

Bien sûr, vous finissez par l'application de la condition de chaque élément dans la liste deux fois. Pour éviter cela, vous pourriez écrire une fonction générique qui ne serait pas aussi nette.

+0

+1 C'est la meilleure façon de le faire à mon avis, sauf indication contraire. – TheFogger

0
Func<string, bool> condition = ...; 
var groupedStrings = strings.GroupBy(condition) 
var stringsMeetingCondition = groupedStrings.FirstOrDefault(g => g.Key); 
var stringsNotMeetingCondition = groupedStrings.FirstOrDefault(g => !g.Key); 
Questions connexes