【问题标题】:LINQ method to sort a list based on a bigger listLINQ 方法根据更大的列表对列表进行排序
【发布时间】:2013-08-04 07:15:29
【问题描述】:
List<int> _lstNeedToOrder = new List<int>();
_lstNeedToOrder.AddRange(new int[] { 1, 5, 6, 8 });

//I need to sort this based on the below list.

List<int> _lstOrdered = new List<int>();//to order by this list
_lstOrdered.AddRange(new int[] { 13, 5, 11, 1, 4, 9, 2, 7, 12, 10, 3, 8, 6 });

order will be -->_lstNeedToOrder = 5,1,8,6

我该怎么做?

【问题讨论】:

    标签: c# linq sorting


    【解决方案1】:

    简单——但效率低——的方法是:

    var result = _lstNeedToOrder.OrderBy(x => _lstOrdered.IndexOf(x));
    

    另一种方法是找出一种方法来获得所需的值索引。如果您的值始终在 [1...n] 范围内,您可以将该“有序”列表反转为“按值列出的索引列表”。此时您可以使用:

    var result = _lstNeedToOrder.OrderBy(x => indexes[x]);
    

    (其中indexes 在开头会有一个额外的值 0,只是为了让事情更简单)。

    或者,您可以创建一个从值到索引的Dictionary&lt;int, int&gt;。这会更普遍,因为它可以处理非常广泛的值而不会占用大量内存。但是字典查找显然比数组或列表查找效率低。

    就像一个旁注,它不会像注释一样格式化,你的初始化可以使用集合初始化器来简化:

    var listToOrder = new List<int> { 1, 5, 6, 8 };
    var orderedList = new List<int> { 13, 5, 11, 1, 4, 9, 2, 7, 12, 10, 3, 8, 6 };
    

    【讨论】:

    • 嘿乔恩,对不起,如果这是一个愚蠢的问题,但为什么第一个效率低下?
    • @DimitarDimitrov:它使用IndexOf 来查找每个条目的所需索引。这是对_lstOrdered 大小的 O(n) 操作,没有必要。
    • @DimitarDimitrov 可能是因为使用了IndexOf?
    • @JonSkeet Awkward 是的,显然......哦,好吧,这非常非常尴尬。
    • @JonSkeet,谢谢乔恩!我希望有一天我能像你一样!
    【解决方案2】:
        List<int> results = _lstOrdered.Where(item => _lstNeedToOrder.Contains(item)).ToList();
    

    【讨论】:

    • 有趣的想法。它不会处理重复项,但我们当然不知道这是否是必需的。
    • 嗯,我没有想到这种情况。 :) 我想这取决于 OP 是否符合要求。
    • @VaughanHilts,我的情况不会重复,谢谢!
    【解决方案3】:

    这很好用:

    var lookup = _lstOrdered
        .Select((x, n) => new { x, n })
        .ToLookup(x => x.x, x => x.n);
    
    var query =
        from x in _lstNeedToOrder
        let rank = lookup[x]
            .DefaultIfEmpty(int.MaxValue)
            .First()
        orderby rank
        select x;
    

    【讨论】:

      【解决方案4】:

      您可以像这样构建自定义比较器:

      public class SequenceComparer<T> : IComparer<T> {
          private readonly Dictionary<T, int> indexes;
      
          public SequenceComparer(IEnumerable<T> sequence) {
              this.indexes =
                  sequence
                      .Select((item, index) => new { Item = item, Index = index })
                      .ToDictionary(x => x.Item, x => x.Index);
          }
      
          public int Compare(T x, T y) {
              return indexes[x].CompareTo(indexes[y]);
          }
      }
      

      现在你可以说

      var result = _lstNeedToOrder.OrderBy(x => x, new SequenceComparer(_lstOrdered));
      

      【讨论】:

      • 如何使用 SequenceComparer&lt;T&gt; 类,我看不出它在 OrderBy 查询中是如何使用的。
      • @King King:谢谢。我错过了在对OrderBy 的调用中创建它。它现在在那里。再次感谢。
      【解决方案5】:

      另一种选择是使用Intersect,它保证按照它们在第一个序列中出现的顺序返回元素。

      所以,在这个例子中

      var result = _lstOrdered.Intersect(_lstNeedToOrder);
      

      根据需要生成 { 5, 1, 8, 6}

      【讨论】:

        【解决方案6】:

        在中间字典中保存顺序...

        // dict key will be the values of _lstOrdered, value will be the index of the
        // key in _lstOrdered
        // I'm using a seldom used .Select overload that returns the current value 
        // plus its index (ix)
        var dict = _lstOrdered.Select((p, ix) => new { Value = p, Ix = ix })
                              .ToDictionary(p => p.Value, p => p.Ix);
        
        // note that this will explode if _lstNeedToOrder contains values outside
        // _lstOrdered.
        _lstNeedToOrder.Sort((p, q) => dict[p] - dict[q]);
        

        .Sort 方法就地排序,因此 _lstNeedToOrder 将被排序。

        【讨论】:

          猜你喜欢
          • 2017-09-30
          • 2023-03-05
          • 2020-12-27
          • 2015-01-08
          • 2013-07-31
          • 1970-01-01
          • 1970-01-01
          • 2020-06-10
          • 2023-04-01
          相关资源
          最近更新 更多