使用 LINQ 将项目移动到列表顶部

Posted

技术标签:

【中文标题】使用 LINQ 将项目移动到列表顶部【英文标题】:Use LINQ to move item to top of list 【发布时间】:2010-12-12 17:05:08 【问题描述】:

有没有办法使用 LINQ 将 id=10 的项目作为列表中的第一项移动?

项目 A - id =5 项目 B - id = 10 项目 C - id =12 项目 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();

【问题讨论】:

您想将项目与顶部项目交换还是通过推动所有项目直到找到的项目向下来旋转项目。 没有;t finalList .insert(0,"neww stuff"); 工作 【参考方案1】:

除了已知的***商品之外,您还想订购什么?如果你不在乎,你可以这样做:

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

基本上,“假”在“真”之前...

诚然,我不知道这在 LINQ to SQL 等中做了什么。您可能需要阻止它在数据库中进行排序:

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

【讨论】:

它不能按预期对 LINQ to SQL 工作。我刚刚测试过。 +1 谢谢乔恩。我想按名称订购,但将 id=0 的项目放在最前面,所以我这样做了: allCountries.OrderBy(x => x.id == 0 ? "00000" : x.Name).ToList();性能不是问题,因为列表很小。 对于稍后查看代码的人来说,布尔值排序为“false, true”可能并不明显。我会推荐更详细的解决方案。 漂亮!我想要一个 Name 在顶部的一个小列表或索引 0 中,在 LINQ to Entities 中,它成功了谢谢+1。 db.Systms.Where(s =&gt; s.IsActive == true).OrderBy(x =&gt; x.SystemName != "Portal Administration").ToList(); 好东西!一个不错的简单解决方案。谢谢,【参考方案2】:

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;

【讨论】:

+1 适用于交换场景,我觉得实际上需要轮换' 这或多或少是我所做的,但感谢您解释为什么似乎没有更好的方法:) 对于错误处理,请注意您应该检查FindIndex结果值,如果在列表中找不到该项目,则为-1。 这不只是将第一个元素与目标元素的索引进行交换,而不是将目标元素移到顶部并将其他所有内容都向下移动吗?【参考方案3】:

Linq 通常在 Enumerables 上工作,所以它现在不是底层类型是一个集合。因此,为了将项目移到列表顶部,我建议使用类似(如果您需要保留订单)

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

如果你的函数只返回一个 IEnumerable,你可以先使用ToList() 方法将其转换为 List

如果您不保留顺序,您可以简单地交换位置 0 和位置 idx 的值

【讨论】:

这非常适合向下旋转场景,而不仅仅是交换值。【参考方案4】:
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id) 

这将在列表顶部插入 id=12 的对象并向下旋转其余对象,保持顺序。

【讨论】:

我喜欢这个思考过程,但是 D 的 ID 为 1,所以这不是按 C、D、A、B 的顺序排列吗? @PhatWrat 当然,所以如果你想避免这种情况,你会说 .ThenBy(o => o.name) 或类似的东西 应该是最佳答案!谢谢【参考方案5】:

这是您可能想要使用的扩展方法。它将与给定谓词匹配的元素移动到顶部,并保留顺序。

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

就复杂性而言,我认为它将对集合进行两次传递,使其成为 O(n),就像插入/删除版本一样,但比 Jon Skeet 的 OrderBy 建议要好。

【讨论】:

【参考方案6】:

您可以使用布尔键将它们“分组”在两个组中,然后对其进行排序

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

【讨论】:

【参考方案7】:

我知道这是一个老问题,但我是这样做的

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

【讨论】:

【参考方案8】:
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;

【讨论】:

【参考方案9】:

有趣的是,您在尝试解决问题时可以找到多种方法。

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

【讨论】:

【参考方案10】:

还要检查是否在没有异常的情况下找到该项目,例如:

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

【讨论】:

【参考方案11】:

如果你有对象就更容易了:

listOfObjects.Remove(object);
listOfObjects.Insert(0, object);

【讨论】:

【参考方案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;

【讨论】:

以上是关于使用 LINQ 将项目移动到列表顶部的主要内容,如果未能解决你的问题,请参考以下文章

快捷方式:将一个项目移动到积压的顶部

Flutter - 选定的项目移动到 ListView 的顶部

将 3 个列表组合成 1 个 LINQ 查询 C#

如何在一个可观察的查找和移动项目到集合的顶部

Dragula:如何始终将项目移动到列表末尾

如何将项目添加到 ListBox 中列表的开头?