2009-11-03 2 views
67

Existe-t-il un moyen de déplacer un élément de dire id = 10 en tant que premier élément d'une liste utilisant LINQ?Utilisez LINQ pour déplacer l'élément vers le haut de la liste

 
Item A - id =5 
Item B - id = 10 
Item C - id =12 
Item D - id =1

Dans ce cas, comment puis-je déplacer avec élégance Point C au sommet de ma collection List<T>?

C'est le meilleur que j'ai en ce moment:

var allCountries = repository.GetCountries(); 
var topitem = allCountries.Single(x => x.id == 592); 
var finalList = new List<Country>(); 
finalList.Add(topitem); 
finalList = finalList.Concat(allCountries.Where(x=> x.id != 592)).ToList(); 
+0

Voulez-vous échanger l'article avec le premier article ou faire pivoter les articles en appuyant sur tous les articles jusqu'à ce que l'article trouvé soit abaissé. – AnthonyWJones

+0

Il suffit de pousser le reste vers le bas – qui

Répondre

45

LINQ est forte dans l'interrogation des collections, créant des projections sur les requêtes existantes ou générer de nouvelles requêtes basées sur des collections existantes. Ce n'est pas un outil pour réordonner les collections existantes en ligne. Pour ce type d'opération, il est préférable d'utiliser le type à la main.

En supposant que vous avez un type avec une définition similaire ci-dessous

class Item { 
    public int Id { get; set; } 
    .. 
} 

Ensuite, effectuez les opérations suivantes

List<Item> list = GetTheList(); 
var index = list.FindIndex(x => x.Id == 12); 
var item = list[index]; 
list[index] = list[0]; 
list[0] = item; 
+2

+1 Fonctionne bien pour le scénario d'échange, je dois sentir qu'une rotation est réellement nécessaire ' – AnthonyWJones

+0

C'est plus ou moins ce que j'ai fait de toute façon, mais merci pour l'explication de pourquoi il y a apparemment pas un meilleur moyen :) – qui

+5

Pour la gestion des erreurs, notez que vous devriez vérifier la valeur de résultat 'FindIndex', c'est -1 si l'élément n'est pas trouvé dans la liste. – schnaader

31

generallyworks Linq sur Enumerables, il n'a donc pas maintenant que le type sous-jacent est un collection. Donc, pour déplacer l'élément en haut de la liste, je suggère d'utiliser quelque chose comme (si vous avez besoin de préserver l'ordre)

var idx = myList.FindIndex(x => x.id == 592); 
var item = myList[idx]; 
myList.RemoveAt(idx); 
myList.Insert(0, item); 

Si votre fonction retourne seulement une IEnumerable, vous pouvez utiliser la méthode ToList() pour le convertir en une liste première

Si vous ne conservez pas l'ordre, vous pouvez simplement échanger les valeurs à la position 0 et la position IDX

+0

Ceci est parfait pour le scénario de rotation vers le bas au lieu de simplement permuter les valeurs. –

114

que voulez-vous commander par, autre que l'élément supérieur connu? Si vous ne vous inquiétez pas, vous pouvez le faire:

var query = allCountries.OrderBy(x => x.id != 592).ToList(); 

Fondamentalement, « false » vient avant « vrai » ...

Il est vrai que je ne sais pas ce que cela fait dans LINQ to SQL, etc. Vous devrez peut-être l'empêcher d'effectuer la commande dans la base de données:

var query = allCountries.AsEnumerable() 
         .OrderBy(x => x.id != 592) 
         .ToList(); 
+1

son ne fonctionne pas comme prévu pour LINQ to SQL. Je viens de le tester. – Yasser

+2

+1 Merci Jon. Je voulais commander par nom mais garder l'élément avec id = 0 sur le dessus donc je l'ai fait: allCountries.OrderBy (x => x.id == 0? "00000": x.Name) .ToList(); la performance n'est pas un problème car la liste est petite. – nima

+2

Pour quelqu'un qui revoit le code plus tard, il peut ne pas être évident que les valeurs booléennes sont classées "false, true". Je recommanderais les solutions plus verbeuses sur ceci. – rymdsmurf

9

Voici une méthode d'extension que vous pouvez utiliser. Il déplace le (s) élément (s) correspondant au prédicat donné vers le haut, en préservant l'ordre.

public static IEnumerable<T> MoveToTop(IEnumerable<T> list, Func<T, bool> func) { 
    return list.Where(func) 
       .Concat(list.Where(item => !func(item))); 
} 

En termes de complexité, je pense que ce serait faire deux passes sur la collecte, le rendant O (n), comme le Insérer/supprimer la version, mais mieux que la suggestion OrderBy de Jon Skeet.

1
public static IEnumerable<T> ServeFirst<T>(this IEnumerable<T> source, 
    Predicate<T> p) 
{ 
    var list = new List<T>(); 

    foreach (var s in source) 
    { 
     if (p(s)) 
      yield return s; 
     else 
      list.Add(s); 
    } 

    foreach (var s in list) 
     yield return s; 
} 
1

C'est intéressant le nombre d'approches que vous trouvez en essayant de résoudre un problème.

var service = AutogateProcessorService.GetInstance(); 
var allConfigs = service.GetAll(); 
allConfigs = allConfigs.OrderBy(c => c.ThreadDescription).ToList(); 
var systemQueue = allConfigs.First(c => c.AcquirerId == 0); 
allConfigs.Remove(systemQueue); 
allConfigs.Insert(0, systemQueue); 
1

Je sais que ce une vieille question, mais je l'ai fait comme ça

class Program 
{ 
    static void Main(string[] args) 
    { 
     var numbers = new int[] { 5, 10, 12, 1 }; 

     var ordered = numbers.OrderBy(num => num != 10 ? num : -1); 

     foreach (var num in ordered) 
     { 
      Console.WriteLine("number is {0}", num); 
     } 

     Console.ReadLine(); 
    } 
} 

cette impression:

Numéro

est 10
nombre est 1
nombre est 5
nombre est- 12

2

Vous pouvez "grouper par" dans deux groupes avec clé booléenne et puis trier les

var finalList= allCountries 
       .GroupBy(x => x.id != 592) 
       .OrderBy(g => g.Key) 
       .SelectMany(g => g.OrderBy(x=> x.id)); 
19
var allCountries = repository.GetCountries(); 
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id) 

Ceci va insérer l'objet avec id = 12 en haut de la liste et faire pivoter le reste vers le bas, la préservation de l'ordre.

+2

Une explication de votre réparation ne ferait pas de mal ... – Markus

+0

Cela a fonctionné pour moi – duyn9uyen

+0

J'aime ce processus de pensée, mais D a un ID de 1, donc ne l'ordonnerait pas comme C, D, A, B ? – David

0

Pour vérifier également si l'élément a été trouvé sans exception, quelque chose comme:

var allCountries = repository.GetCountries(); 
var lookup = allCountries.ToLookup(x => x.id == 592); 
var finalList = lookup[true].Concat(looup[false]).ToList(); 
if (lookup[true].Count != 1) YouAreInTrouble(); 
0

J'ai écrit une méthode d'extension statique pour le faire. Notez que cela ne préserve pas l'ordre, il échange simplement l'élément. Si vous avez besoin de conserver la commande, vous devriez faire une rotation pas un simple échange.

/// <summary> 
/// Moves the item to the front of the list if it exists, if it does not it returns false 
/// </summary> 
/// <typeparam name="T"></typeparam> 
/// <param name="collection"></param> 
/// <param name="predicate"></param> 
/// <returns></returns> 
public static bool MoveToFrontOfListWhere<T>(this List<T> collection, Func<T, bool> predicate) 
{ 
    if (collection == null || collection.Count <= 0) return false; 

    int index = -1; 
    for (int i = 0; i < collection.Count; i++) 
    { 
     T element = collection.ElementAt(i); 
     if (!predicate(element)) continue; 
     index = i; 
     break; 
    } 

    if (index == -1) return false; 

    T item = collection[index]; 
    collection[index] = collection[0]; 
    collection[0] = item; 
    return true; 
} 
Questions connexes