如何从字典中的列表中选择单个值?

时间:2016-12-03 18:34:29

标签: c# linq dictionary

我的数据结构如下。

Dictionary<Blopp, List<double>> data = ...;

人们可以看到,它确实是一种字典列表。而现在,我需要独立访问它们。所以我希望得到这样的东西。

for(int i = 0; i < data.First().Value.Count; i++)
{
  Dictionary<Blopp, double> partial = ...;
  // do stuff to the partial number i
}

我该怎么做?

我用谷歌搜索但是有点难以用goolenglish来解释我正在寻找什么,所以我得到了杰克。 :(

修改

显然,不仅谷歌搜索令人困惑,因此,正如所建议的那样,我提供了一个基于数据的例子。

之前的状态:

  

{A:{q,w,e,r},B:{a,s,d,f},C:{z,x,c,v}}

状态请求外循环的第一次迭代。

  

{A:q,B:a,C:z}

状态请求外循环的第二次迭代。

  

{A:w,B:s,C:x}

State请求外循环的第三次迭代。

  

{A:e,B:d,C:c}

在上面的伪示例中,大写代表键,而小写代表双键。

2 个答案:

答案 0 :(得分:1)

你可以这样做:

var firstEntry = data.First();
for(int i = 0; i < firstEntry.Value.Count; i++)
{
  Dictionary<Blopp, double> partial = data.ToDictionary(x => x.Key, x => x.Value[i]);
  // do stuff to the partial number i
}

或者您可以使用LINQ将整个数据转换为单独的词典列表:

List<Dictionary<Blopp,double>> listOfDict = 
Enumerable.Range(0,data.First().Value.Count)
          .Select(i => data.ToDictionary(x => x.Key, x => x.Value[i]))
          .ToList();

编辑:

之前的两种方法都会创建大量的一次性字典。如果您不需要修改它们并且只需要执行查找,那么这当然效率低下且无用。所以,我会寻找一种利用一些包装类的扩展方法:

static class MultiDictionaryExtension
{
    #region Extension
    public static IEnumerable<IDictionary<TK, TV>> AsSeparatedDictionaries<TK, TV>(this IDictionary<TK, List<TV>> multiDict)
    {
        int numDictionaries = multiDict.First().Value.Count;
        for (int i = 0; i < numDictionaries; i++)
            yield return new SingleDictionaryWrap<TK, TV>(multiDict, i);
    }
    #endregion

    #region Helper classes
    public class SingleDictionaryWrap<TK, TV> : IDictionary<TK, TV>
    {
        private class ValueCollection : ICollection<TV>
        {
            private readonly SingleDictionaryWrap<TK, TV> dict;
            public ValueCollection(SingleDictionaryWrap<TK, TV> dict)
            {
                this.dict = dict;
            }
            public int Count { get { return this.dict.Count; } }
            public bool IsReadOnly { get { return false; } }
            public void Add(TV item) { throw new NotSupportedException("This dictionary is readonly"); }
            public void Clear() { throw new NotSupportedException("This dictionary is readonly"); }
            public bool Contains(TV item) { return this.dict.Select(x => x.Value).Contains(item); }
            public void CopyTo(TV[] array, int arrayIndex) { foreach (var item in this) array[arrayIndex++] = item; }
            public IEnumerator<TV> GetEnumerator() { return this.dict.Select(x => x.Value).GetEnumerator(); }
            public bool Remove(TV item) { throw new NotSupportedException("This dictionary is readonly"); }
            IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
        }

        private readonly IDictionary<TK, List<TV>> multiDict;
        public int Index { get; private set; }
        public SingleDictionaryWrap(IDictionary<TK, List<TV>> multiDict, int index)
        {
            this.Index = index;
            this.multiDict = multiDict;
        }

        public ICollection<TK> Keys { get { return this.multiDict.Keys; } }
        public ICollection<TV> Values { get { return new ValueCollection(this); } }
        public int Count { get { return this.multiDict.Count; } }
        public bool IsReadOnly { get { return true; } }
        public TV this[TK key]
        {
            get { return this.multiDict[key][this.Index]; }
            set { throw new NotSupportedException("This dictionary is readonly"); }
        }
        public bool ContainsKey(TK key) { return this.multiDict.ContainsKey(key); }
        public void Add(TK key, TV value) { throw new NotSupportedException("This dictionary is readonly"); }
        public bool Remove(TK key) { throw new NotSupportedException("This dictionary is readonly"); }
        public bool TryGetValue(TK key, out TV value)
        {
            value = default(TV);
            List<TV> values;
            if (this.multiDict.TryGetValue(key, out values))
            {
                value = values[this.Index];
                return true;
            }
            return false;
        }
        public void Add(KeyValuePair<TK, TV> item) { throw new NotSupportedException("This dictionary is readonly"); }
        public void Clear() { throw new NotSupportedException("This dictionary is readonly"); }
        public bool Contains(KeyValuePair<TK, TV> item)
        {
            TV value;
            if (this.TryGetValue(item.Key, out value))
            {
                return Object.Equals(value, item.Value);
            }
            return false;
        }
        public void CopyTo(KeyValuePair<TK, TV>[] array, int arrayIndex) { foreach (var kvp in this) array[arrayIndex++] = kvp; }
        public bool Remove(KeyValuePair<TK, TV> item) { throw new NotSupportedException("This dictionary is readonly"); }
        public IEnumerator<KeyValuePair<TK, TV>> GetEnumerator() { return this.multiDict.Select(kvp => new KeyValuePair<TK, TV>(kvp.Key, kvp.Value[this.Index])).GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
    }
    #endregion
}

相当长的扩展类,但看看它如何使一切变得更简单:

foreach (var partial in dict.AsSeparatedDictionaries())
{
    // use partial as a normal IDictionary<Blopp,double>
    // if you need the current "i" use partial.Index
}

免责声明:
所有以前的代码都假设所有内部列表具有完全相同的长度。

答案 1 :(得分:0)

我相信这会做你想要的。可能有一种更简单的方法可以做到这一点,如果我想到它我会更新答案,即使内部列表的长度不同,这段代码也能正常工作。

static void Main(string[] args)
{
    Dictionary<Blopp, List<double>> data = CreateData();

    List<Dictionary<Blopp, double>> result = PiviotDictionary(data).ToList();
}

private static IEnumerable<Dictionary<TKey, TValue>> GetSingleEntires<TKey,TValue>(Dictionary<TKey, List<TValue>> data)
{
    bool foundValue = true;
    for(int i = 0; foundValue == true; i++)
    {
        foundValue = false;
        var result = new Dictionary<TKey, TValue>();
        foreach (var kvp in data)
        {
            if (kvp.Value.Count > i)
            {
                foundValue = true;
                result.Add(kvp.Key, kvp.Value[i]);
            }
        }

        if (foundValue)
            yield return result;

    }
}