簡體   English   中英

字典可以用不同的鍵排序嗎?

[英]Can a Dictionary be sorted by a different key?

我需要按鍵從數據結構中檢索項目。 但我還要求使用除鍵之外的字段以排序順序遍歷該數據結構。

像這樣的東西(偽代碼,僅用於說明目的):

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

我該怎么做? 有沒有辦法使用單一數據結構,還是我需要自己滾動?

注意: TItem已經從(並實現) IComparable<T>派生。

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

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

請記住,這實際上是在創建一個新的集合,而不是對原始集合進行排序(將在SortedDictionary中進行維護)。

SortedDictionary類有一個Values屬性 ,“獲取包含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);
            }
        }
    }
}

將項添加到SortedList只會存儲對內存中已有對象的引用,因此不會占用更多空間。

您可以使用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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM