【问题标题】:Can a Dictionary be sorted by a different key?字典可以按不同的键排序吗?
【发布时间】:2010-10-18 17:34:38
【问题描述】:

我需要通过键从数据结构中检索项目。但我还需要使用键以外的字段以排序顺序遍历该数据结构。

类似这样的东西(伪代码,仅用于说明目的):

var list = new SortedDictionary<TKey, TSortField, TItem>();

我该怎么做?有没有办法使用单一的数据结构,还是我需要自己动手?

注意:TItem 已经派生自(并实现)IComparable&lt;T&gt;

【问题讨论】:

  • 您是否总是必须使用原始结构?在给定特定字段的需要时,您始终可以使用 IEnumerable.Sort 和 ToDictionary() 的组合进行排序。
  • @贾斯汀:谢谢。你能把这个作为答案发布吗?看起来它可能比我想象的要简单。

标签: c# design-patterns data-structures


【解决方案1】:

如果您不打算修改排序结果,则可以使用 IEnumerable.OrderBy 和 ToDictionary 的组合:

var sortedResult = sortedDictionary.OrderBy(kvp => kvp.Value.SortField)
                                   .ToDictionary(kvp => kvp.Key,
                                                 kvp => kvp.Value);

请记住,这实际上是在创建一个新集合,而不是对原始集合进行排序(将在 SortedDictionary 中维护)。

【讨论】:

    【解决方案2】:

    SortedDictionary 类有一个Values property,它“获取包含 SortedDictionary 中的值的集合”。该集合是一个 IEnumerable,您可以对该值集合进行排序。

    这是一个 C# 示例程序(我作为示例快速编写了它。它可能可以根据您的特定需求进行改进和/或更改)。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                SortedDictionary<string, int> sd = new SortedDictionary<string, int>();
    
                sd.Add("a", 10);
                sd.Add("c", 4);
                sd.Add("b", 20);
    
                Console.WriteLine("___KEYS____");
                foreach (string key in sd.Keys)
                {
                    Console.WriteLine(key);
                }
    
                foreach(int sortedVal in sd.Values.OrderBy(value => value))
                {
                    Console.WriteLine(sortedVal);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      将项目添加到 SortedList 只会存储对已在内存中的对象的引用,因此不会占用更多空间。

      【讨论】:

        【解决方案4】:

        您可以使用 LINQ 对字典的 Values 属性进行排序,但如果列表很大或排序功能很昂贵,这可能会比您希望的要慢 - 特别是如果您经常访问它。

        这是我对排序字典的快速而肮脏的实现。它使用 Dictionary 进行直接访问,但也保留 ListDictionary 实例以对键或值进行排序访问。

        您可以对任何字段或表达式进行排序。但是,如果您的排序包括方法调用,则函数应该是确定性的(因此给定一组值,它们总是以相同的方式排序)。另请注意,项目在插入时进行排序 - 如果对象随后更新,排序顺序不会自动更新。如果有可能,最好使用 LINQ 按需排序。

        public class ArbitrarySortedDictionary<TKEY, TSORT, TVALUE> : IDictionary<TKEY, TVALUE> 
            where TSORT : IComparable
            where TKEY : IComparable
        {
            /// <summary>
            /// Key class for use in keeping the _SortedKeys and _SortedValues in proper order.  Since the sorting 
            /// function could easily result in same values across instances, we'll use the key as secondary sort
            /// field - since it's unique, everything should always have a consistent, unambiguous sort order.
            /// </summary>
            class SortedDictionaryKey
            {
                public SortedDictionaryKey(TSORT sortVal, TKEY key)
                {
                    SortVal = sortVal;
                    Key = key;
                }
        
                public TSORT SortVal
                {
                    get;
                    private set;
                }
        
                public TKEY Key
                {
                    get;
                    private set;
                }
            }
        
            readonly Func<TVALUE, TSORT> _SortFunc;
            readonly Dictionary<TKEY, TVALUE> _InternalDict = new Dictionary<TKEY, TVALUE>();
            readonly SortedList<SortedDictionaryKey, TKEY> _SortedKeys;
            readonly SortedList<SortedDictionaryKey, TVALUE> _SortedValues;
        
        
            public ArbitrarySortedDictionary(Func<TVALUE, TSORT> sortFunc)
            {
                _SortFunc = sortFunc;
        
                Func<SortedDictionaryKey, SortedDictionaryKey, Int32> compFunc = (x, y) => {
                    Int32 sortValComp = 0;
        
                    if (x.SortVal != null)
                        sortValComp = x.SortVal.CompareTo(y.SortVal);
        
                    if (sortValComp != 0)
                        return sortValComp;
        
                    if (x.Key != null)
                        return x.Key.CompareTo(y.Key);
        
                    return y.Key == null ? 0 : -1;
                };
        
                var comparer = new LambdaComparer<SortedDictionaryKey>(compFunc);
        
                _SortedKeys = new SortedList<SortedDictionaryKey, TKEY>(comparer);
                _SortedValues = new SortedList<SortedDictionaryKey, TVALUE>(comparer);
            }
        
            public void Add(TKEY key, TVALUE value)
            {
                if (key == null)
                    throw new ArgumentException("Null key is not allowed.");
        
                var sortKey = new SortedDictionaryKey(_SortFunc(value), key);
        
                _InternalDict.Add(key, value);
                _SortedKeys.Add(sortKey, key);
                _SortedValues.Add(sortKey, value);
            }
        
            public bool ContainsKey(TKEY key)
            {
                return _InternalDict.ContainsKey(key);
            }
        
            public ICollection<TKEY> Keys
            {
                get {
                    return _SortedKeys.Values.ToList<TKEY>();
                }
            }
        
            public bool Remove(TKEY key)
            {
                return RemoveInternal(key, _InternalDict[key]);
            }
        
            public bool TryGetValue(TKEY key, out TVALUE value)
            {
                return _InternalDict.TryGetValue(key, out value);
            }
        
            public ICollection<TVALUE> Values
            {
                get {
                    return _SortedValues.Values.ToList<TVALUE>();
                }
            }
        
            public TVALUE this[Int32 index]
            {
                get {
                    return _InternalDict[_SortedKeys.Values[index]];
                }
                set {
                    throw new NotImplementedException("Can't update ArbitrarySortedDictionary directly.");
                }
            }
        
            public TVALUE this[TKEY key]
            {
                get {
                    return _InternalDict[key];
                }
                set {
                    if (!ContainsKey(key)) {
                        Add(key, value);
                        return;
                    }
        
                    throw new NotImplementedException("To update items currently, remove and re-add.");
                }
            }
        
            public void Add(KeyValuePair<TKEY, TVALUE> item)
            {
                var sortKey = new SortedDictionaryKey(_SortFunc(item.Value), item.Key);
        
                _InternalDict.Add(item.Key, item.Value);
                _SortedKeys.Add(sortKey, item.Key);
                _SortedValues.Add(sortKey, item.Value);
            }
        
            public void Clear()
            {
                _SortedKeys.Clear();
                _SortedValues.Clear();
                _InternalDict.Clear();
            }
        
            public bool Contains(KeyValuePair<TKEY, TVALUE> item)
            {
                var val = _InternalDict[item.Key];
        
                if (val == null)
                    return item.Value == null;
        
                return val.Equals(item.Value);
            }
        
            public void CopyTo(KeyValuePair<TKEY, TVALUE>[] array, int arrayIndex)
            {
                Int32 curIndex = arrayIndex;
        
                foreach (var item in this)
                    array[curIndex++] = item;
            }
        
            public int Count
            {
                get { return _InternalDict.Count; }
            }
        
            public bool IsReadOnly
            {
                get { return false; }
            }
        
            public bool Remove(KeyValuePair<TKEY, TVALUE> item)
            {
                return RemoveInternal(item.Key, item.Value);
            }
        
            public IEnumerator<KeyValuePair<TKEY, TVALUE>> GetEnumerator()
            {
                var res =
                    from k in _SortedKeys
                    join v in _SortedValues on k.Key equals v.Key
                    orderby k.Key
                    select new KeyValuePair<TKEY, TVALUE>(k.Value, v.Value);
        
                return res.GetEnumerator();
            }
        
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        
        
            private Boolean RemoveInternal(TKEY key, TVALUE val)
            {
                if (!_InternalDict.Remove(key))
                    return false;
        
                var sortKey = new SortedDictionaryKey(_SortFunc(val), key);
                var retVal = _SortedKeys.Remove(sortKey);
                return retVal && _SortedValues.Remove(sortKey);
            }
        }
        
        
        
        
        
        /// <summary>
        /// Utility class - an IComparer based upon a lambda expression.
        /// </summary>
        public class LambdaComparer<T> : IComparer<T>
        {
            private readonly Func<T, int> _lambdaHash;
            private readonly Func<T, T, int> _lambdaComparer;
        
            public LambdaComparer(Func<T, T, int> lambdaComparer) :
                this(lambdaComparer, o => 0)
            {
            }
        
            public LambdaComparer(Func<T, T, int> lambdaComparer, Func<T, int> lambdaHash)
            {
                if (lambdaComparer == null)
                    throw new ArgumentNullException("lambdaComparer");
        
                if (lambdaHash == null)
                    throw new ArgumentNullException("lambdaHash");
        
                _lambdaHash = lambdaHash;
                _lambdaComparer = lambdaComparer;
            }
        
            public int Compare(T x, T y)
            {
                return _lambdaComparer(x, y);
            }
        
            public int GetHashCode(T obj)
            {
                return _lambdaHash(obj);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2021-11-27
          • 2011-06-17
          • 1970-01-01
          • 1970-01-01
          • 2015-10-10
          • 2012-07-30
          • 1970-01-01
          • 2017-08-20
          相关资源
          最近更新 更多