За допомогою LINQ перемістіть елемент у верхній частині списку


81

Чи є спосіб перемістити елемент, скажімо id = 10, як перший елемент у списку за допомогою LINQ?

Елемент A - id = 5
Елемент B - id = 10
Елемент C - id = 12
Елемент D - id = 1

У цьому випадку, як я можу елегантно перемістити елемент С у верх моєї List<T>колекції?

Це найкраще, що я маю зараз:

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

Ви хочете поміняти елемент на верхній елемент або повернути елементи, натискаючи всі елементи, поки знайдений предмет не буде знищений.
AnthonyWJones

didn; t finalList .insert (0, "neww stuff"); робота
Рохіт Кумар

Відповіді:


52

LINQ ефективно справляється із запитами до колекцій, створює прогнози щодо існуючих запитів або генерує нові запити на основі існуючих колекцій. Він не призначений як інструмент для впорядкування існуючих колекцій в рядку. Для такого типу операції найкраще використовувати підручний тип.

Припускаючи, що у вас тип із подібним визначенням, як показано нижче

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

Тоді спробуйте наступне

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 Добре працює для сценарію обміну, я повинен відчувати, що насправді потрібен поворот
AnthonyWJones

Це більш-менш те, що я зробив будь-яким способом, але дякую за пояснення, чому, здається, не кращий спосіб :)
qui

6
Для обробки помилок зверніть увагу, що слід перевірити FindIndexзначення результату, яке дорівнює -1, якщо елемент не знайдено у списку.
schnaader

Хіба це не просто обмін першого елемента з індексом цільового елемента, а не переміщення цільового елемента вгору та переміщення всього іншого вниз?
frostshoxx

143

Що ви хочете замовити, крім відомого найвищого товару? Якщо вам все одно, ви можете зробити це:

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

В основному, "false" стоїть перед "true" ...

Слід визнати, що я не знаю, що це робить у LINQ to SQL тощо. Можливо, вам доведеться зупинити його від упорядкування в базі даних:

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

1
це не працює належним чином для LINQ to SQL. Я просто тестував це.
Ясер Шайх,

5
+1 Дякую Йон. Я хотів замовити за назвою, але залишити товар з ідентифікатором = 0 зверху, тому я зробив це: allCountries.OrderBy (x => x.id == 0? "00000": x.Name) .ToList (); продуктивність не є проблемою, оскільки список невеликий.
німа

3
Для когось, що переглядає код пізніше, може бути не очевидно, що логічні значення впорядковані "false, true". Я б порадив більш детальні рішення щодо цього.
rymdsmurf

1
Гарний! Я хотів Nameу невеликому списку зверху або в Індексі 0, у LINQ to Entities, і це зробило фокус Спасибі +1. db.Systms.Where(s => s.IsActive == true).OrderBy(x => x.SystemName != "Portal Administration").ToList();
Ірфан

Чудові речі! Приємне просте рішення. Дякую,
Уго Нава Копп

43

Linq, як правило, працює над Перечислюваними, тому зараз це не так, що базовим типом є колекція. Тому для переміщення елемента у верхній частині списку я б запропонував використовувати щось на зразок (якщо вам потрібно зберегти замовлення)

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

Якщо ваша функція повертає лише IEnumerable, ви можете скористатися ToList()методом, щоб спочатку перетворити його в Список

Якщо ви не зберегли порядок, ви можете просто поміняти місцями значення в позиції 0 та позиції idx


Це ідеально підходить для сценарію повороту вниз замість того, щоб просто міняти місцями значення.
Бредлі Маунтфорд,

35
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id) 

Це додасть об’єкт з id = 12 у верхню частину списку, а решту поверне вниз, зберігаючи порядок.


Мені подобається цей процес мислення, але D має ідентифікатор 1, тож чи не буде це впорядковувати як C, D, A, B?
Девід

3
@PhatWrat Звичайно, тому, якщо ви хочете цього уникнути, скажіть .ThenBy (o => o.name) або щось подібне
Нік Гіллум

Має бути головною відповіддю! Дякую
Мантісімо

10

Ось метод розширення, який ви можете використовувати. Він переміщує елементи, що відповідають даному предикату, у верхню частину, зберігаючи порядок.

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

Що стосується складності, я думаю, це зробить два пропуски колекції, зробивши її O (n), як версія Вставити / Вилучити, але краще, ніж Замовлення Джона Скіта За пропозицією.


2

Ви можете "згрупувати за" дві групи за допомогою логічного ключа, а потім відсортувати їх

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

2

Я знаю це давнє запитання, але я зробив це так

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

це друкує:

число 10
число 1
число 1 число 5
число 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

Цікава кількість підходів, які ви знайдете, намагаючись вирішити проблему.

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

Щоб також перевірити, чи був предмет знайдений без винятку, приблизно так:

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

Для цього я написав метод статичного розширення. Зверніть увагу, що це не зберігає замовлення, воно просто замінює товар. Якщо вам потрібно було зберегти замовлення, вам слід зробити обертання, не простий обмін.

/// <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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.