2009-11-03 13 views
67

Czy istnieje sposób na przeniesienie elementu id = 10 jako pierwszego elementu na liście przy użyciu LINQ?Użyj LINQ, aby przenieść pozycję na górę listy

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

W tym przypadku, jak można elegancko poruszać pozycja C na szczycie mojej List<T> kolekcji?

To najlepsze mam teraz:

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

Czy chcesz zamienić przedmiot na górny element lub obrócić elementy, przesuwając wszystkie elementy, aż znaleziony przedmiot w dół. – AnthonyWJones

+0

Wystarczy wcisnąć resztę dół – qui

Odpowiedz

45

LINQ jest silny w kwerend kolekcje, tworzenie prognoz w stosunku do istniejących zapytań lub generowania nowych zapytań opartych na istniejących kolekcji. Nie jest to narzędzie do ponownego zamawiania istniejących kolekcji inline. W przypadku tego typu operacji najlepiej jest używać typu pod ręką.

Zakładając, że typ z podobnym definicją poniżej

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

należy spróbować następujących

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 działa dobrze dla scenariusza wymiany, muszę czujesz, że obracanie jest rzeczywiście wymagany tho” – AnthonyWJones

+0

To jest mniej więcej to, co zrobiłem w dowolny sposób, ale dzięki za wyjaśnienie, dlaczego nie ma pozornie nie lepszy sposób :) – qui

+5

do obsługi błędów, trzeba pamiętać, że należy sprawdzić 'FindIndex' wartość wynikową, to -1, jeśli element nie znajduje się w wykazie. – schnaader

31

Linq generallyworks na Enumerables, więc nie teraz, że typ bazowy jest kolekcja. Więc do przesuwania elementu na szczycie listy Sugerowałbym użyciu coś jak (jeśli trzeba zachować kolejność)

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

Jeśli funkcja zwraca tylko IEnumerable, można użyć metody ToList() aby przekształcić go pierwszy

List Jeśli nie zachować kolejność można po prostu zamienią się wartości w pozycji 0 i pozycji idx

+0

Jest to idealne rozwiązanie do scenariusza obracania, a nie tylko do zamiany wartości. –

114

Co chcesz zamówić przez inne niż znane górnym sprzęcie? Jeśli nie obchodzi, można to zrobić:

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

Zasadniczo, „false” jest przed „prawdziwym” ...

Wprawdzie nie wiem co to robi w LINQ to SQL etc Może być konieczne zatrzymanie go od wykonania zamówienia w bazie danych:

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

jego nie działa zgodnie z oczekiwaniami dla LINQ do SQL. Właśnie to przetestowałem. – Yasser

+2

+1 Dzięki Jon. Chciałem zamówić wg nazwy ale utrzymać pozycję z id = 0 na górze więc zrobiłem to: allCountries.OrderBy (x => x.id == 0 "00000": x.Name) .ToList(); wydajność nie jest problemem, ponieważ lista jest niewielka. – nima

+2

Dla kogoś przeglądu kodu później może nie być oczywiste, że wartości logiczne są sortowane „false, true”. Polecam bardziej rozwlekłe rozwiązania. – rymdsmurf

9

Oto metoda rozszerzenia, z której możesz skorzystać. Przesuwa element (e) pasujący do podanego predykatu do góry, zachowując kolejność.

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

Pod względem złożoności, myślę, że byłoby to zrobić dwa karnety na zbiorach, dzięki czemu O (n), jak Wstaw/Usuń wersję, ale lepiej niż Jon Skeet za OrderBy sugestii.

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

Interesująca liczba metod, jakie można znaleźć, próbując rozwiązać problem.

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

wiem, że to jest stare pytanie, ale zrobiłem to tak

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(); 
    } 
} 

Drukuje:

numer 10
numer 1
numer 5
numer jest 12

2

można "grupa przez" w dwie grupy z kluczem Boolean, a następnie posortować je

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) 

To wstawić obiekt o id = 12 u góry listy i obrócić resztę w dół, zachowując kolejność.

+2

Wyjaśnienie twojej poprawki nie zaszkodzi ... – Markus

+0

To zadziałało dla mnie – duyn9uyen

+0

Uwielbiam ten proces myślowy, ale D ma identyfikator 1, więc czy to nie byłoby takie jak C, D, A, B ? – David

0

również sprawdzić, czy element został znaleziony bez wyjątku, coś jak:

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

pisałem statyczną metodę rozszerzenia tego robić. Zauważ, że to nie zachowuje zamówienia, po prostu zamienia przedmiot. Jeśli chcesz zachować porządek, powinieneś wykonać obrót, a nie zwykłą zamianę.

/// <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; 
} 
Powiązane problemy