【问题标题】:Compare values in a dictionary of dictionaries比较字典中的值
【发布时间】:2014-10-01 22:33:49
【问题描述】:

我有一本字典如下:

Dictionary<string, Dictionary<string, double>> 

<A, <2011, 100>>
    <2012, 125>>
    <2013, 142>>
<B, <2011, 350>>
    <2012, 340>>
    <2013, 400>>
<C, <2011, 75>>
    <2012, 80>>
    <2013, 102>>

我将如何遍历这本字典来计算年份或年份增长?增长计算如下:

Growth = (current year count - (previous year count))/(previous year count)
       = (125 - 100)/100
       = 0.25

所以新字典中的结果是:

<A, <2011, ->>
    <2012, 0.25>>
    <2013, 0.14>>
<B, <2011, ->>
    <2012, -0.03>>
    <2013, 0.18>>
<C, <2011, ->>
    <2012, 0.07>>
    <2013, 0.28>>

现在我对计算增长的兴趣不如在学习如何循环遍历该字典以比较同一字典中的值然后将结果输出到新字典中那么感兴趣。还有其他应用程序的完整列表,这将非常有用。我最初的想法是在 for 循环中做一个 for 循环,但后来我不知道如何回到前一年,因为键不是标准的 0、1、2、3。我正在使用 C# MVC4 进行开发。

感谢大家的帮助。

【问题讨论】:

    标签: c# asp.net-mvc logic


    【解决方案1】:

    首先,我对您为什么想要/需要在 Dictionary 中创建此结构非常感兴趣。字典是非常具体的集合,旨在用于特定情况,我不确定你是否需要。所以我建议你考虑创建一个简单的类型列表:

    new List<MyType>
    {
        new MyType { Letter = "A", Year = 2011, Value = 100D },
        new MyType { Letter = "A", Year = 2012, Value = 125D }
    };
    

    如果你没有考虑到这一点,你可以创建一个匿名类型的列表来计算:

    var objList = dic
        .SelectMany(s => s.Value.Select(t => new
        {
            Letter = s.Key,
            Year = t.Key,
            Value = t.Value
        }))
        .ToList();
    

    然后你可以这样计算你想要的:

    var result = objList
        .Select(current =>
        {
            var last = objList
                .FirstOrDefault(f => f.Letter == current.Letter
                    && f.Year == current.Year - 1);
    
            return new
            {
                Letter = current.Letter,
                Year = current.Year,
                Calculated = last != null && last.Value != 0
                    ? (current.Value - last.Value) / last.Value
                    : (double?)null
            };
        })
        .ToList();
    

    如果你最后真的需要一本字典,你可以这样做:

    var newDic = result
        .GroupBy(g => g.Letter)
        .ToDictionary(k => k.Key, v => v.ToDictionary(
            k2 => k2.Year, v2 => v2.Calculated));
    

    如果只考虑性能和内存,这不是更好的答案。但是,如果您想要一个更动态且更易于解释的解决方案,那么这个答案就很好了。

    【讨论】:

      【解决方案2】:

      不幸的是,除了将键复制到数组并在数组中向后索引之外,没有简单的方法可以向后迭代 Dictionary(或 SortedDictionary)。 (Linq 方法Enumerable.Reverse 实际上就是这样做的。)

      如果您需要在这些年中向后和向前迭代,而不是 Dictionary&lt;string, Dictionary&lt;string, double&gt;&gt;,我建议使用 SortedList&lt;TKey, TValue&gt; 作为您的内部集合:

      Dictionary<string, SortedList<string, double>>     
      

      甚至更好

      Dictionary<string, SortedList<DateTime, double>>
      

      这样做的好处是Keys 属性实现了Ilist&lt;TKey&gt;,因此您可以执行IndexOf,这将是log-n。如果您需要进行邻近查询(即查找最接近给定年份的年份),您可以推出自己的二分搜索:

          public static int BinarySearch<T>(this IList<T> list, T value, IComparer<T> comparer)
          {
              // Adapted from http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
              if (list == null)
                  throw new ArgumentNullException("list");
              comparer = comparer ?? Comparer<T>.Default;
              int lo = 0;
              int hi = list.Count - 1;
              while (lo <= hi)
              {
                  int i = lo + ((hi - lo) >> 1);
                  int order = comparer.Compare(list[i], value);
      
                  if (order == 0)
                      return i;
                  if (order < 0)
                  {
                      lo = i + 1;
                  }
                  else
                  {
                      hi = i - 1;
                  }
              }
      
              return ~lo;
          }
      

      SortedListDictionary 相比的缺点是,如果您无序插入大量项目,则插入时间是 n 平方的。根据您的描述,我怀疑这对您来说不是问题。

      更新

      鉴于您每年的 SortedList 值,这里有一些您可以使用的扩展:

          /// <summary>
          /// Enumerate through all items in the list between first and last, inclusive.  First and last need not be in the list.
          /// </summary>
          public static IEnumerable<KeyValuePair<TKey, TValue>> Between<TKey, TValue>(this SortedList<TKey, TValue> list, TKey first, TKey last)
          {
              if (list == null)
                  throw new ArgumentNullException();
              var comparer = list.Comparer;
              var index = list.Keys.BinarySearch(first, comparer);
              if (index < 0) // There can be no duplicated keys in SortedList.
                  index = ~index;
              for (int count = list.Count; index < count; index++)
              {
                  var key = list.Keys[index];
                  if (comparer.Compare(key, last) > 0)
                      break;
                  yield return new KeyValuePair<TKey, TValue>(key, list.Values[index]);
              }
          }
      
          public static SortedList<TKey, double> ComputeGrowth<TKey>(this SortedList<TKey, double> list)
          {
              var count = list.Count;
              SortedList<TKey, double> newList = new SortedList<TKey, double>(count, list.Comparer);
              for (int i = 0; i < count; i++)
              {
                  if (i == 0)
                      newList.Add(list.Keys[i], 0.0); // Is this what you want?
                  else
                      newList.Add(list.Keys[i], (list.Values[i] - list.Values[i - 1]) / list.Values[i - 1]); // Any need to check for division by zero?
              }
              return newList;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-20
        • 1970-01-01
        • 2023-01-07
        • 1970-01-01
        相关资源
        最近更新 更多