【问题标题】:Modifying duplicate Key value on entry to Dictionary (C#)修改字典条目上的重复键值(C#)
【发布时间】:2017-02-22 00:55:35
【问题描述】:

我有一个要求,我想使用字典来存储“key=value”对,但是现在我需要存储“重复”键值。

我有一行分隔文本(以竖线字符 ("|") 分隔,我将其拆分为一个数组,然后拆分为一个字典(见下文)

string[] t = rawMessage.Split(new[] { '|', '|' }, StringSplitOptions.RemoveEmptyEntries);
            message = t.ToDictionary(s => s.Split('=')[0], s => s.Split('=')[1]);

如果不存在重复项,这可以正常工作,但我想以一种在插入前调整键值的方式存储重复键。

例子

A=1|B=2|C=3|D=4|D=5|D=5

我希望将键值调整为:

A=1
B=2
C=3
D=4
D[1]=5
D[2]=5

然后我可以提取第二个和第三个“D”条目的条目,因为它们有一个新的键值。

每个重复项都是一个不完整的记录,因此我希望能够按照输入的顺序引用它们。例如,第四个条目是 D[4]。关于对所有值使用相同键值的建议,可能/将会令人困惑,我最终可能会提取错误的信息(但请记住)。

我想避免事先循环遍历数组,并想知道是否有人知道我可以在上面代码的 ToDictionary 部分执行此操作的方法。

对于相当基本的解释表示歉意。 许多线程都处理删除字典中的欺骗,我知道这不是字典的预期用途。

【问题讨论】:

  • 您必须设计自己的数据结构来跟踪重复项。或者按照@itsme86 所说的去做。但这取决于您要如何检索该信息。
  • 字典的消费者如何知道他们想要第一个 D 键还是第二个?
  • Quantic - 第一个条目是 D,第二个是 D[1],第三个是 D[2],依此类推。 “[]”的格式只是唯一插入密钥的东西。然后他们会输入他们想看到的键。

标签: c# dictionary


【解决方案1】:

如果您想将多个值与一个键相关联,您可以将值对的一部分设为一个列表:

var dict = new Dictionary<char, List<int>>();

char key = 'D';
int value = 5;

if (!dict.ContainsKey(key))
    dict[key] = new List<int>();
dict[key].Add(value);

【讨论】:

  • 这听起来是个不错的解决方案。以前做过。很多次。
  • 感谢您的评论。问题是每个重复键都是单独的记录,因此将它们捆绑在一起意味着我将失去根据 unqiue 值提取数据的能力。我会更详细地更新问题。
  • @Arclight 我不确定我是否遵循。您可以通过 dict['D'][1] 获得第二个 D 条目。
【解决方案2】:

您可以使用GroupBy 按键对结果集进行分组,然后使用SelectMany, 展平分组,使用带有索引参数的Select 方法重载投影所需的键:

var message = t
    .Select(s => s.Split('='))
    .GroupBy(s => s[0], s => s[1])
    .SelectMany(g => g.Select((v, i) => new
    {
        Key = i == 0 ? g.Key : g.Key + "[" + i + "]",
        Value = v
    }))
    .ToDictionary(e => e.Key, e => e.Value);

【讨论】:

    【解决方案3】:

    您可以使用具有基于集合的值的字典:

    Dictionary<string, List<int>>
    

    然后,您可以根据需要使用最佳数据结构,但List&lt;&gt; 是一个很好的默认值。

    或者您可以使用 Lookup&lt;TKey, TElement&gt; 类,但这不会为值元素提供数组索引:

    Lookup<string, int>
    

    它支持键下的分组(因此可以枚举TElement)。它的文档是here


    另外,您也可以使用自己的数据结构,但这不太可能需要 - 现有事物的组合可能对您足够好。

    【讨论】:

    • @Fabio 够公平的。我会把它留给你的评论添加 - 除了能够按插入顺序索引之外,OP 并没有真正说明使用情况,Lookup 实际上并没有提供任何一种方式,但我感觉那是一个 XY 问题。
    【解决方案4】:

    嗯..我要一个基本的答案,不使用 LINQ。我要做的是创建自己的ToDictionary 方法,只需检查密钥是否存在,如果存在,则创建新密钥并将其添加到Dictionary,如下所示:

    public Dictionary<string,string> ToDictionary(string[] t)
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();
        foreach (string s in t)
        {
            string[] reg = s.Split('=');
            int i = 1;
            while (dict.ContainsKey(reg[0]))
            {
                if (!reg[0].Contains('['))
                    reg[0] = reg[0] + "[" + i.ToString() + "]";
                else
                    reg[0] = reg[0].Replace((i - 1).ToString(), i.ToString());
                i++;
            }
    
            dict.Add(reg[0], reg[1]);
        }
        return dict;
    }
    

    这可以很容易地转换为扩展方法。

    【讨论】:

      【解决方案5】:

      另一种方法是在 System.Collections.Specialized 中使用 NameValueCollection。 NameValueCollection 的有趣之处在于,如果同一个键有重复值,它会将值与逗号分隔值保持在同一个键中。这样,键将是相同的,但您可能必须将其分开。 下面给出了一个快速示例和输出。

      class Program
      {
          static void Main(string[] args)
          {
              NameValueCollection col = new NameValueCollection();
              col.Add("A", "1");
              col.Add("A", "2");
              col.Add("B", "0");
              col.Add("B", "1");
              col.Add("B", "3");
              col.Add("D", "1");
              foreach (string key in col.AllKeys)
              {
                  Console.WriteLine(key + " " + col[key] + "\n");
              }
              Console.ReadKey();
          }
      }
      

      输出 一个 1,2 乙 0,1,2 1 Output

      【讨论】:

        【解决方案6】:

        就像 Adam Houldsworth 说的,你可以使用 Lookup

        这是基于 Adam Houldsworth 的回答的 sn-p。

        var str = @"A=1|B=2|C=3|D=4|D=5|D=5";
        string[] t = str.Split(new[] { '|', '|' }, StringSplitOptions.RemoveEmptyEntries);
        var message = t.ToLookup(s => s.Split('=')[0], s => Convert.ToInt32(s.Split('=')[1]));
        var res = message.ToDictionary(i=>i.Key, x=>x.LastOrDefault());
        

        编辑:

        再次阅读您的问题后,我知道您也想要 D=4 值。

        var res = message.SelectMany(i => i.Select((x, y) => new
        {
            Key = i.Key,
            Value = x
        })).GroupBy(q=>q.Value)
           .SelectMany(x=>x.ToLookup(xx=>xx.Key, xx=>xx.Value));
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-27
          • 1970-01-01
          • 1970-01-01
          • 2011-05-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多