Использовать LINQ, чтобы переместить элемент на верхней части списка


есть ли способ переместить элемент say id=10 в качестве первого элемента в списке с помощью LINQ?

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

в этом случае как я могу элегантно переместить элемент C в верхнюю часть моего 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();
11 70

11 ответов:

LINQ сильна в запросе коллекций, создании проекций по существующим запросам или создании новых запросов на основе существующих коллекций. Он не предназначен в качестве инструмента для переупорядочения существующих коллекций inline. Для этого типа операции лучше всего использовать тип в hande.

предполагая, что у вас есть тип с аналогичным определением, как показано ниже

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;

что вы хотите заказать, кроме известного верхнего элемента? Если вам все равно, вы можете сделать это:

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

в основном, " ложь "предшествует"истине"...

по общему признанию, я не знаю, что это делает в LINQ to SQL и т. д. Возможно, вам придется остановить его от выполнения заказа в базе данных:

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

Linq обычно работает с перечисляемыми, поэтому теперь он не является базовым типом коллекции. Поэтому для перемещения элемента в верхней части списка я бы предложил использовать что-то вроде (Если вам нужно сохранить порядок)

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

Если ваша функция возвращает объект IEnumerable, вы можете использовать ToList() метод, чтобы преобразовать его в список первых

Если вы не сохраняете порядок, вы можете просто поменять значения в позиции 0 и позиции idx

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

Это позволит вставить объект с id=12 в верхней части списка и повернуть остальные вниз, сохраняя порядок.

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

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

С точки зрения сложности, я думаю, что он сделает два прохода по коллекции, сделав ее O(n), как версия Insert/Remove, но лучше, чем предложение OrderBy Джона Скита.

вы можете "сгруппировать" в две группы с Булевым ключом, а затем отсортировать их

var finalList= allCountries
                .GroupBy(x => x.id != 592)
                .OrderBy(g => g.Key)
                .SelectMany(g => g.OrderBy(x=> x.id ));
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;
}

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

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

Я знаю, что это старый вопрос, но я сделал это так

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

Я написал статический метод расширения для этого. Обратите внимание, что это не сохраняет заказ, он просто меняет элемент. Если вам нужно было сохранить порядок, вы должны сделать поворот не простой своп.

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

чтобы проверить, если элемент был найден без исключения, что-то вроде:

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