【问题标题】:How to use ToDictionary with <string, object> dictionary如何将 ToDictionary 与 <string, object> 字典一起使用
【发布时间】:2015-12-05 19:35:25
【问题描述】:

我正在尝试创建一个每个键可以保存多个值的字典,并且我创建了一个名为Pair 的类,它由两个strings 组成。我已将idDictionary 定义为包含string 作为键,Pair 作为值,但我不确定如何编写ToDictionary 语句,因为这个概念对我来说是新概念,我找不到这方面的任何例子。

Dictionary<string, Pair<string, string>> idDictionary = new Dictionary<string, Pair<string, string>>();

我知道使用常规的通用字典我会简单地使用这样的东西:

idDictionary = resultData.Rows.Select(row => row.Split(','))
                              .ToDictionary(id => id[0], id => id[1]);

我不确定如何为名为@9​​87654329@ 的对象实现类似的东西。也许我错过了一些非常简单的东西,但非常感谢那些有答案的人。

编辑以包含完整的代码块和更详尽的解释

原始代码块在这里(带有通用字典)。我更改此设置的原因是,如果每个键的值超过 1 个,则应用程序会因重复键而出错。

    private List<ImportItem<T>> ProcessReportResult(CSVTable resultData, ICollection<ImportItem<T>> data, Func<T, string> keyFilter)
    {
        WriteLog("{1}{0} records found.{1}", resultData.Rows.Length, Environment.NewLine);

        //key = Order Number; value = Order ID
        var idDictionary = resultData.Rows.Select((row => row.Split(','))).ToDictionary(id => id[0], id => id[1]);
        idDictionary.ForEach(id => WriteLog("Input Id = {0} - Matching record Id = {1}", id.Key, id.Value));
        var processList = data.Where(item => idDictionary.ContainsKey(keyFilter(item.DataItem))).ToList();
        processList.ForEach(item => item.id = idDictionary[keyFilter(item.DataItem)]);
        return processList;
    }

【问题讨论】:

  • 只使用Tuple有什么问题?
  • 你想成为什么关键和价值?
  • 您能否展示一些示例数据以及您期望得到的结果?
  • 我刚刚修改了帖子中的一些错误。
  • 加上元组。您还可以根据您的全部要求将列表或集合视为值 (Dictionary>)

标签: c# string object dictionary


【解决方案1】:

可以通过分组来实现获得一对多键值存储的一般解决方案,但这需要将value 作为项目列表。如果我尝试通过您给定的示例进行解释,则可以通过以下方式创建将行转换为每个键多值存储的查询:

        idDictionary = 
         resultData.Rows
            .GroupBy(row => row.Id, row => row.Split(','))
            .ToDictionary(g => g.Key, g => g.ToList());

更新:

针对您的问题的具体解决方案。假设数据的结构类似于:

 List<Row> rows = new List<Row>{
            new Row{
                values = "1,A"
            },
            new Row{
                values = "2,C,D,E"
            },
            new DataRow{
                values = "3,E,X,CV,B"
            },
        };

您也可以在这里使用 Group 来获取 Key、Value(List)。注意,这里我跳过了第一个值,它已经在索引 0 处被捕获为键。

     var idDictionary =
                 rows.GroupBy(row => row.values.Split(',')[0], 
                                  row => row.values.Split(',').Skip(1))
                                 .ToDictionary(g => g.Key, g => g.ToList());

这会给你这样的结果:

    /* output - 

     |1, (A)|
     |2, (C,D,E)|
     |3, (E,X,CV,B)|

     */

虽然您必须更改通过 List 获取值的实现。 但是,如果每个键找到多个值,此解决方案将阻止程序。

【讨论】:

  • Vendetta,您能否提供一个示例,说明如何通过列表更改获取值?到目前为止,这是一个很大的帮助。
  • 很高兴它有帮助!对于获取值,您必须将值视为列表,例如idDictionary.ForEach(id =&gt; WriteLog("Input Id = {0} - Matching record Id = {1}", id.Key, id.Value)); 这里 id.Value 将是一组值,因此如果列表有多个值,您可能需要处理它们。如果列表只有一个值,那么 id.Value.First() 会起作用,对于多个值,您可以根据您的业务逻辑来决定。
  • 有趣的是,与其将字典拆分为字符串,不如列出键、值的组合,然后我可以告诉它只获取第一个值而不是创建一个列表吗?
  • 是的,您可以应用 First() 扩展方法来仅获取第一个值而不是完整列表。例如` .ToDictionary(g => g.Key, g => g.ToList().First());`
【解决方案2】:

不确定您到底需要什么,也许这个简单的例子会有所帮助?

 idDictionary = resultData.Rows
                   .Select((row => row.Split(',')))
                   .ToDictionary<string, Pair<string, string>>
                           (id => id[0],id => new Pair(id[1],id[1]));

此版本的 ToDictionary 采用两个函数,一个返回键,另一个返回枚举中每个项目的值。

【讨论】:

  • ToDictionary 的第二个参数不应该是id =&gt; new Pair(id[1],id[1])。
【解决方案3】:

如果您知道每行中有多少项,或者您需要考虑每行可能有不同数量的项,您必须决定是否需要基于元组的方法(甚至是对)。

// Setup sample data
var resultData = new
{
    Rows = new string[] { "1,A,B,C", "2,A,B", "3,A,B,C,D" }
};

// If same length for each row, tuple would work easily
// Dictionary<string, Tuple<string, string>>
var tuples = resultData.Rows
    .Select(r => r.Split(','))
    .ToDictionary(
        r => r[0],
        r => Tuple.Create(r[1], r[2])
    );

// If length is variable, then some type of collection could be better
// Dictionary<string, List<string>> 
var lists = resultData.Rows
    .Select(r => r.Split(','))
    .ToDictionary(
        r => r[0],
        r => r.Skip(1).ToList() // Skip adding id element
    );

这是要比较的第一项的输出:

?lists["1"]
Count = 3
    [0]: "A"
    [1]: "B"
    [2]: "C"
?tuples["1"]
{(A, B)}
    Item1: "A"
    Item2: "B"

【讨论】:

    【解决方案4】:

    原始代码块在这里(带有通用字典)。我更改此设置的原因是,如果每个键的值超过 1 个,则应用程序会因重复键而出错。

    看来你要找的是ToLookup

    "Lookup&lt;TKey, TElement&gt; 表示一组键,每个键映射到一个或多个值。"

    .

    var idDictionary = resultData.Rows.Select((row => row.Split(',')))
                       .ToLookup(id => id[0], id => id[1]);
    

    编辑

    一个简短的示例:

    var lines = new string[] { "a,b", "a,c", "d,e" };
    var dict = lines.Select(line => line.Split(','))
                .ToLookup(x => x[0], x => x[1]);
    
    result: 
        Key: a   Value: [b,c]
        Key: e   Value: [e]
    

    示例用法:

    Console.WriteLine(string.Join(",", dict["a"]));
    

    【讨论】:

    • Eser,谢谢你,我已经尝试过这个解决方案,但我最终得到了“找不到密钥”错误。也许我做错了什么,因为我从未使用过 ToLookup,你能提供一个更详细的例子吗?其余被阻塞的部分需要如何更改?我并不反对这一点,但很难理解这种变化是如何与整个区块一起工作的。
    • @booksguy 非常类似于字典。你到底想用查找/字典做什么?一个简短的示例,展示如何获得 key not found?
    • var idDictionary = resultData.Rows.Select((row => row.Split(','))).ToLookup(id => id[0], id => id[1]) ; idDictionary.ForEach(id => WriteLog("输入Id = {0} - 匹配记录Id = {1}", id.Key)); var processList = data.Where(item => idDictionary.Contains(keyFilter(item.DataItem))).ToList(); processList.ForEach(item => item.Id = idDictionary[keyFilter(item.DataItem)]);返回进程列表;
    • @booksguy 您的代码甚至无法编译。查看我的编辑。
    • 我已经粘贴了我处理 ToLookup 更改的方式,但我知道我做得不对。为了更清楚我的需求,应用程序将 CSV 中的第一列作为键,向系统请求匹配值。由于一些不可预见的问题,系统在某些情况下每个键都有多个值。我需要修复这段代码,以便它可以遍历每个值而不是键,并将注释(CSV 的第二列)提交给每个值。我希望这有意义,如果没有,我会编辑。
    猜你喜欢
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 2012-05-20
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多