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

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;

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

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


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.


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.


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

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


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

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

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

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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.