【问题标题】:Sort list based on calculated index of properties根据计算的属性索引排序列表
【发布时间】:2018-12-13 02:50:08
【问题描述】:

我有以下课程。

 public class Header
    {
        public long CombinedIndex { get; private set; }
        public int Key { get; set; } //Unique
        public bool IsRequired { get; set; }// True or false
        public DateTime? AvailableDate { get; set; } // null or date value
        public int Index { get; set; } // a number and can be same among other Header 

        public void CalculateCombinedIndex()
        {
            CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
        }
    }

我预计大约 50,000 个实例 它在一个数组中。我需要按以下顺序对它们进行排序:

  • 首先基于 IsRequired
  • 然后索引
  • 那么可用日期(日期值接近当前日期)
  • 键(主键)

将所有这些属性值和 导出一个唯一的数字,然后只对该数字进行排序。 稍后,可以将 Header 的列表转换为字典 where 键是 CombinedIndex,我已经排序的数组可以查找 字典顺序我迭代了数组。如何高效和 承诺这一切看起来?

【问题讨论】:

    标签: c# .net sorting indexing


    【解决方案1】:

    只要实现IComparable,就可以使用List.Sort()。

    public class Header : IComparable
    {
        public long CombinedIndex { get; private set; }
        public int Key { get; set; } //Unique
        public bool IsRequired { get; set; }// True or false
        public DateTime? AvailableDate { get; set; } // null or date value
        public int Index { get; set; } // a number and can be same among other Header 
    
        public void CalculateCombinedIndex()
        {
            CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
        }
    
        public int CompareTo(object obj)
        {
            if (obj == null) return 1;
    
            Header otherHeader = obj as Header;
            if (otherHeader != null)
            {
                if (this.IsRequired && !otherHeader.IsRequired)
                    return 1;
                if (!this.IsRequired && otherHeader.IsRequired)
                    return -1;
                if (this.Index > otherHeader.Index)
                    return 1;
                if (this.Index < otherHeader.Index)
                    return -1;
                //// ....
                return 0;
            }
            else
                throw new ArgumentException("Object is not a Temperature");
        }
    }
    

    【讨论】:

    • 使用Comparision<T>IComparer<T> 重载Sort 会减少对类的干扰。
    • @KennethK。相同的基本思想。在课堂上可以很方便。
    • 嗯,是的,也不是。是的,它实现了目标,但是没有,您现在已经锁定了比较逻辑。当您想以不同的方式比较项目时会发生什么?您只能实现一次接口。您现在是否发表了一些巨大的case 声明(例如)?
    • @KennethK。不打算和你辩论。 OP可以决定他想走哪条路线。
    • 在 Icomparable over Comparision 和 IComparer 之间进行选择听起来很有趣。但是,在我做出决定之前,您认为使用 CombinedIndex 有什么缺点吗?我认为与这里提到的两种方法相比,它会更快更轻量级。有什么想法吗?
    【解决方案2】:

    我会用 LINQ 来做:

    var ordered = list
        .OrderByDescending(x => x.IsRequired)
        .ThenBy(x => x.Index)
        .ThenBy(x=> x.AvailableDate)
        .ThenBy(x=> x.Key);
    

    只有在平局的情况下才会使用下一个条件。所以不需要创建一个组合索引,这使得更改顺序逻辑非常困难,而且似乎无论如何都被破坏了。

    【讨论】:

      【解决方案3】:

      如果您需要根据给定类型的投影键执行比较操作,我发现拥有一个可以从Func&lt;TSource, TKey&gt; 委托创建IComparer&lt;TSource&gt; 实例的KeyComparer 类会很有帮助。这特别适用于 C# 7 中引入的新元组语法。以下是生成的语法示例:

      // Create an IComparer<Header> instance based on your combined key.
      var comparer = KeyComparer.Create((Header h) => (h.IsRequired, h.Index, h.AvailableDate, h.Key));
      
      List<Header> headers = ...
      
      // Sort the list using the combined key.
      headers.Sort(comparer);
      
      // Convert to a dictionary keyed by the combined key.
      var dict = headers.ToDictionary(comparer.KeySelector);
      

      这是该类的示例实现。 (更详细的解释见my blog post。)

      public static class KeyComparer
      {
          public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
              Func<TSource, TKey> keySelector,
              IComparer<TKey> innerComparer = null)
          {
              return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
          }
      }
      
      public class KeyComparer<TSource, TKey> : Comparer<TSource>
      {
          protected internal KeyComparer(
              Func<TSource, TKey> keySelector,
              IComparer<TKey> innerComparer = null)
          {
              KeySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector));
              InnerComparer = innerComparer ?? Comparer<TKey>.Default;
          }
      
          public Func<TSource, TKey> KeySelector { get; }
          public IComparer<TKey> InnerComparer { get; }
      
          public override int Compare(TSource x, TSource y)
          {
              if (object.ReferenceEquals(x, y))
                  return 0;
              if (x == null)
                  return -1;
              if (y == null)
                  return 1;
      
              TKey xKey = KeySelector(x);
              TKey yKey = KeySelector(y);
              return InnerComparer.Compare(xKey, yKey);
          }
      }
      

      【讨论】:

      • KeyComparer.Create 方法在 .net 4.6.2 中的表现如何?我的意思是说不使用元组语法。
      • 我想是这样的: var comparer = KeyComparer.Create((Header h) => Tuple.Create(!h.IsRequired, h.Index, h.AvailableDate, h.Key));
      • @JayNanavaty:没错。由于Tuple.Create 实例化了一个引用类型,因此它的性能比 C# 7 版本稍差,但这对于 50,000 条记录来说并不重要。
      • 有没有办法指定h.Index、h.AvailableDate等升序或降序?
      • @JayNanavaty:我认为除了使用其组件的默认比较器之外,不可能对元组进行排序。如果您不介意掺杂键的值,则可以应用否定来实现该效果:KeyComparer.Create((Header h) =&gt; Tuple.Create(!h.IsRequired, -h.Index, -h.AvailableDate.Ticks, h.Key)); 否则,您必须创建自定义比较器。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-19
      • 2021-11-30
      • 2020-03-19
      • 1970-01-01
      • 1970-01-01
      • 2013-01-02
      • 2018-07-08
      相关资源
      最近更新 更多