0

I'm trying to return an ordered version of a ConcurrentDictionary<string, ConcurrentQueue> using the below code:

    public ConcurrentDictionary<string, ConcurrentQueue<decimal>> 
    GetBestList(ConcurrentDictionary<string, ConcurrentQueue<decimal>> inputList)
    {
        var bestList = new ConcurrentDictionary<string, ConcurrentQueue<decimal>>(
                inputList.Where(x => x.Value != null && x.Value.Count >= 2).
                OrderByDescending(x => CalculateAverage(x.Value)));

        return bestList;
    }

    public decimal CalculateAverage(ConcurrentQueue<decimal> inputList)
    {
        return inputList.Average();
    }

The above code returns a jumbled mess and doesn't keep the same ordering so I tried to change it. I found this question and used the code from the answer to try this and it works correctly with keeping the correct ordering but it is a Dictionary and not a ConcurrentDictionary

var bestList = inputList.Where(x => x.Value != null && x.Value.Count >= 2).
               OrderByDescending(x => CalculateAverage(x.Value)).
               ToDictionary(pair => pair.Key, pair => pair.Value);

I then found this question and used code from the answer to create an extension method for ToConcurrentQueue but it also doesn't keep the same ordering and returns a jumbled mess.

public static class ConcurrentDictionaryExtensions
{
    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");

        ConcurrentDictionary<TKey, TElement> d = new ConcurrentDictionary<TKey, TElement>(comparer ?? EqualityComparer<TKey>.Default);
        foreach (TSource element in source)
            d.TryAdd(keySelector(element), elementSelector(element));

        return d;
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, null);
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, comparer);
    }

    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
    {
        return ToConcurrentDictionary<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
    }

    internal class IdentityFunction<TElement>
    {
        public static Func<TElement, TElement> Instance
        {
            get { return x => x; }
        }
    }

}

I have exhausted all of my ideas so I'm hoping someone here can let me know how I can return a ConcurrentDictionary using the same ordering.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
DarthVegan
  • 1,719
  • 7
  • 25
  • 42
  • Have you tried passing a normal dictionary as argument to the constructor of the `ConcurrentDictionary`? I think it should work. – Theodor Zoulias Oct 12 '20 at 20:28
  • @Servy the question [proposed as duplicate](https://stackoverflow.com/questions/20020074/is-the-list-order-of-a-concurrentdictionary-guaranteed) is an "Is" question. This one is a "How" question. If I write an answer for this question and post it as an answer to the other question, it will be off topic there. – Theodor Zoulias Oct 12 '20 at 20:35
  • No, it wouldn't. The answer to this question is strictly the answer in the duplicate. As the duplicate says, A concurrent dictionary is unordered, you must use a different collection that *can* be ordered. That's it, as the duplicate says. Your "solution" is just wrong. If it wasn't, it would *also* be an answer to the duplicate (so the questions are still duplicates regardless). – Servy Oct 12 '20 at 20:36
  • @Servy on second thought I think you are right. The answer to this question is: "it's not possible". – Theodor Zoulias Oct 12 '20 at 22:17
  • DarthVegan if you are interested for my opinion about the `ConcurrentDictionary` in general, is that it's rarely the best tool for the job. You can read my arguments [here](https://stackoverflow.com/questions/42013226/when-should-i-use-concurrentdictionary-and-dictionary/63940194#63940194). – Theodor Zoulias Oct 12 '20 at 22:21
  • @TheodorZoulias I'm not sure why my question was closed but yes I would love your opinion. I do need to use a ConcurrentDictionary because like I said before I'm reading and writing to it from different threads and when I used a regular dictionary it failed. I created a workaround with my code so I found my own answer to my question anyway – DarthVegan Oct 13 '20 at 00:47
  • DarthVegan the question was closed because what you are trying to do is to convert an unordered `ConcurrentDictionary` to an ordered one. But there is no such thing as an ordered `ConcurrentDictionary` (it is unordered by nature), so what you are trying to do is impossible. And this would be the only valid answer to this question. A `ConcurrentDictionary` is a useful tool for the narrow goal of reading/writing from multiple threads, but depending on what the "big picture" is, there may be better tools for the job available. – Theodor Zoulias Oct 13 '20 at 01:37
  • @TheodorZoulias alright that makes sense – DarthVegan Oct 13 '20 at 01:38

0 Answers0