Verwenden Sie LINQ, um das Element an den Anfang der Liste zu verschieben

80

Gibt es eine Möglichkeit, ein Element mit der Bezeichnung id = 10 als erstes Element in einer Liste mithilfe von LINQ zu verschieben?

Punkt A - id = 5
Punkt B - ID = 10
Punkt C - id = 12
Punkt D - id = 1

Wie kann ich in diesem Fall Artikel C elegant an den Anfang meiner List<T>Sammlung verschieben?

Dies ist das Beste, was ich derzeit habe:

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();
qui
quelle
Möchten Sie den Gegenstand gegen den obersten Gegenstand austauschen oder die Gegenstände drehen, indem Sie alle Gegenstände bis zum gefundenen Gegenstand nach unten drücken.
AnthonyWJones
didn; t finalList .insert (0, "neww stuff"); Arbeit
Rohit Kumar

Antworten:

51

LINQ ist stark darin, Sammlungen abzufragen, Projektionen über vorhandene Abfragen zu erstellen oder neue Abfragen basierend auf vorhandenen Sammlungen zu generieren. Es ist nicht als Werkzeug gedacht, um vorhandene Sammlungen inline neu zu ordnen. Für diese Art von Operation ist es am besten, den Typ bei Hand zu verwenden.

Angenommen, Sie haben einen Typ mit einer ähnlichen Definition wie unten

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

Versuchen Sie dann Folgendes

List<Item> list = GetTheList();
var index = list.FindIndex(x => x.Id == 12);
var item = list[index];
list[index] = list[0];
list[0] = item;
JaredPar
quelle
4
+1 Funktioniert gut für das Swap-Szenario. Ich muss das Gefühl haben, dass tatsächlich eine Drehung erforderlich ist, um
AnthonyWJones
Dies ist mehr oder weniger das, was ich getan habe, aber danke für die Erklärung, warum es anscheinend keinen besseren Weg gibt :)
qui
6
Beachten Sie bei der Fehlerbehandlung, dass Sie den FindIndexErgebniswert überprüfen sollten. Er ist -1, wenn das Element nicht in der Liste gefunden wird.
Schnaader
Tauschen Sie nicht einfach das erste Element mit dem Zielelementindex aus, anstatt das Zielelement nach oben zu verschieben und alles andere nach unten zu verschieben?
Frostshoxx
140

Womit möchten Sie außer dem bekannten Top-Artikel bestellen? Wenn es Sie nicht interessiert, können Sie dies tun:

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

Grundsätzlich kommt "falsch" vor "wahr" ...

Zugegeben, ich weiß nicht, was dies in LINQ to SQL usw. bewirkt. Möglicherweise müssen Sie verhindern, dass die Bestellung in der Datenbank ausgeführt wird:

var query = allCountries.AsEnumerable()
                        .OrderBy(x => x.id != 592)
                        .ToList();
Jon Skeet
quelle
1
Es funktioniert nicht wie erwartet für LINQ to SQL. Ich habe es gerade getestet.
Yasser Shaikh
5
+1 Danke Jon. Ich wollte nach Namen bestellen, aber den Artikel mit der ID = 0 oben behalten, also habe ich Folgendes getan: allCountries.OrderBy (x => x.id == 0? "00000": x.Name) .ToList (); Leistung ist kein Problem, da die Liste klein ist.
Nima
3
Für jemanden, der den Code später überprüft, ist es möglicherweise nicht offensichtlich, dass die Booleschen Werte "falsch, wahr" sind. Ich würde die ausführlicheren Lösungen dazu empfehlen.
Rymdsmurf
1
Wunderschönen! Ich wollte eine Namein einer kleinen Liste oben oder Index 0 in LINQ to Entities, und es hat den Trick gemacht Danke +1. db.Systms.Where(s => s.IsActive == true).OrderBy(x => x.SystemName != "Portal Administration").ToList();
Irfaan
Tolles Zeug! Eine schöne einfache Lösung. Vielen Dank,
Hugo Nava Kopp
43

Linq arbeitet im Allgemeinen mit Enumerables, sodass der zugrunde liegende Typ jetzt keine Sammlung mehr ist. Um den Artikel ganz oben auf die Liste zu setzen, würde ich empfehlen, etwas wie (wenn Sie die Reihenfolge beibehalten müssen) zu verwenden.

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

Wenn Ihre Funktion nur eine IEnumerable zurückgibt, können Sie diese ToList()Methode verwenden, um sie zuerst in eine Liste zu konvertieren

Wenn Sie die Reihenfolge nicht beibehalten, können Sie einfach die Werte an Position 0 und Position idx tauschen

Grizzly
quelle
Dies ist perfekt für das Szenario zum Herunterdrehen, anstatt nur die Werte auszutauschen.
Bradley Mountford
35
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id) 

Dadurch wird das Objekt mit der ID = 12 oben in der Liste eingefügt und der Rest nach unten gedreht, wobei die Reihenfolge beibehalten wird.

Nick Gillum
quelle
Ich liebe diesen Denkprozess, aber D hat eine ID von 1, würde das nicht C, D, A, B sein?
David
3
@PhatWrat Sicher, wenn Sie vermeiden möchten, dass Sie sagen würden. DannBy (o => o.name) oder etwas ähnliches
Nick Gillum
Sollte die beste Antwort sein! Danke
Mantisimo
10

Hier ist eine Erweiterungsmethode, die Sie möglicherweise verwenden möchten. Es verschiebt die Elemente, die dem angegebenen Prädikat entsprechen, nach oben, wobei die Reihenfolge beibehalten wird.

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

In Bezug auf die Komplexität denke ich, dass es zwei Durchgänge für die Sammlung machen würde, was sie zu O (n) macht, wie die Version zum Einfügen / Entfernen, aber besser als Jon Skeets OrderBy-Vorschlag.

Konfigurator
quelle
2

Sie können mit dem Booleschen Schlüssel in zwei Gruppen "gruppieren" und diese dann sortieren

var finalList= allCountries
                .GroupBy(x => x.id != 592)
                .OrderBy(g => g.Key)
                .SelectMany(g => g.OrderBy(x=> x.id ));
Filip
quelle
2

Ich weiß, dass dies eine alte Frage ist, aber ich habe es so gemacht

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

dies druckt:

Nummer ist 10
Nummer ist 1
Nummer ist 5
Nummer ist 12

Gaotter
quelle
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;
}
Grozz
quelle
1

Es ist interessant, wie viele Ansätze Sie finden, wenn Sie versuchen, ein Problem zu lösen.

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);
Adeola Ojo Gabriel
quelle
1

Um auch zu überprüfen, ob der Artikel ohne Ausnahme gefunden wurde, gehen Sie wie folgt vor:

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

Ich habe dazu eine statische Erweiterungsmethode geschrieben. Beachten Sie, dass dadurch die Reihenfolge nicht erhalten bleibt, sondern der Artikel einfach ausgetauscht wird. Wenn Sie die Reihenfolge beibehalten möchten, sollten Sie eine Drehung und keinen einfachen Tausch durchführen.

/// <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;
}
Rollen
quelle