2

我有多个动态对象(大多数时候)只有几个值不同。我希望能够将这些对象合并到一个对象中,如果发生冲突(两个值不相同),那么我希望将它们存储在一个集合或另一个动态对象中(如果可能的话)。

我正在使用 expandoObject 类,因此我可以将我的对象转换为字典并尝试合并它,但我还没有找到任何关于合并字典的文章或资源,这些字典会创建有冲突的集合。

有没有办法以一种易于实施和高效的方式做到这一点?

我将发布一些代码示例,让您了解我正在尝试完成的工作

//returns a dynamic object from JSON (output from DataBase)
JsonReader reader = new JsonReader();
dynamic object = reader.Read(input);

//Creates a dictionairy with all the fieldnames and values.
IDictionary<string, dynamic> properties = (IDictionary<string, dynamic>)object;

//My goal is to merge multiple of these objects into one single object and
//creating collections when values are different from each other.
4

3 回答 3

3

您的问题似乎基于 IDictionary<> 接口。它假设每个键只有一个值。我相信您更愿意转向IEnumerable<IGrouping<TKey,TValue>>表示键值集合列表的 LINQ-ish。

当您执行 .GroupBy 或 .ToLookup 调用时,LINQ 会发出此类对象。
那就玩吧:

using System.Linq;

Dictionary<string, dynamic> A = ...;
Dictionary<string, dynamic> B = ...;

// naiive atempt:
var lookup = 
    A.Keys.Concat(B.Keys)
        .ToDictionary(key => key, key => new dynamic[]{ A[key], B[key] } ));

当然它不会起作用,但我已经写了它来看看它会暴露什么问题。首先 - 你可能会在连接时得到重复的键。那么并不是所有的键都在 A 和 B 中,所以索引器会抛出。接下来,我碰巧假设原始对象是字符串到一值的,并且您可能会处理已经碰撞的对象。然后,这仅使用 2 个 A/B 对象,而您可能想要处理多个..

IEnumerable<Dictionary<string, dynamic>> inputs = ....;

// btw. GropuBy returns a lookup, too :) a key -> all matches
var matched_by_keys = inputs.GroupBy(pair => pair.Key);

var final = matched_by_keys.ToDictionary(group => group.Key, group => group);

看这里。我已经使用了所有字典,并根据它们的键“将它们配对”。结果,我得到了一个查找,将每个现有的键绑定到一系列之前分配给该键的值。结果matched_by_keys是一个 Enumerable,而不是字典,所以后来被翻译成它。查看 ToDictionary 的参数:Group 本身就是一个 IEnumerable,只需指出一个键,并且该组保持不变。

尽管如此,输入仅适用于单值的 IDictionary。如果您也需要对多值输入执行此类操作,您可以轻松地将 IDictionaries 转换为查找并对它们执行操作:

IEnumerable<Dictionary<string, dynamic>> inputs1 = ....; // normal items
IEnumerable<ILookup<string, dynamic>> inputs2 = ....; // items that previously already collided

var allAsLookups =
    inputs1.ToLookup(pair => pair.Key, pair => pair.Value)
    .Concat( inputs2 );

// btw. GropuBy returns a lookup, too :) a key -> all matches
var matched_by_keys = lookups.GroupBy(lk => lk.Key);

var final1 = matched_by_keys.ToDictionary(group => group.Key, group => group.SelectMany(s=>s));

请注意现在必须翻译最后的组。在此示例中, groupis 不是IEnumerable<Value>,而是因为允许IEnumerable<IEnumerable<Value>>输入是多值的,即使它们只有 1 个值。所以,它必须被展平,这是由 SelectMany 完成的。这反过来不需要投影任何东西,因为该项目已经是 IEnumerable,所以 =>s 就足够了。

使用 GroupBy、ToLookup 和 ToDictionary 的不同重载,您可以实现许多有用的效果。玩超载!

于 2012-08-15T12:32:28.720 回答
1

由于值是 adynamic你确实可以存储一个值或一个集合,只要你认为合适。

我们需要某种字典集合,在本例中是一个数组。

var props1 = new Dictionary<string, dynamic> 
    {
        {"uniq1", "uniq1"},
        {"same", "same1"},
    };
var props2 = new Dictionary<string, dynamic> 
    {
        {"uniq2", "uniq2"},
        {"same", "same2"},
    };
var props3 = new Dictionary<string, dynamic> 
    {
        {"uniq3", "uniq3"},
        {"same", "same3"},
    };

var props = new[] { props1, props2, props3 };

然后获取第一个并将其与以下合并。如果一个键已经存在,那么检查它是否已经是一个 List 或者我们是否必须创建一个。如果密钥不存在,则只需添加它。

var merged = props.First();

foreach (var prop in props.Skip(1))
{
    foreach (var key in prop.Keys)
    {
        dynamic oldValue;
        if (merged.TryGetValue(key, out oldValue))
        {
            if (oldValue is IList<dynamic>)
                merged[key].Add(prop[key]);
            else
                merged[key] = new List<dynamic> { oldValue, prop[key] };
        }
        else
            merged.Add(key, prop[key]);
    }
}

merged将设置为:

merged
Count = 4
    [0]: {[uniq1, uniq1]}
    [1]: {[same, System.Collections.Generic.List`1[System.Object]]}
    [2]: {[uniq2, uniq2]}
    [3]: {[uniq3, uniq3]}
merged["same"]
{System.Collections.Generic.List<object>}
    [0]: "same1"
    [1]: "same2"
    [2]: "same3"
于 2012-08-15T13:35:28.053 回答
1

如果您的动态对象是Expando,您可以使用 Linq 如下:

var objectList = new List<dynamic>() {...};

var result = objectList.SelectMany(obj => 
                           (IDictionary<string, dynamic>)obj.ToList())
            .GroupBy(pair => pair.Key)
            .ToDictionary(group => group.Key,
                          group => group.Select(pair => pair.Value));

首先,使用从动态对象SelectMany中选择所有KeyValuePair<string, dynamic>,这种方式将接受具有相同键的列表。

然后,按 Key 分组并使用函数ToDictionary来获取结果。

于 2012-08-15T15:10:51.700 回答