拼合类属性中的字典

时间:2018-10-19 12:23:21

标签: c#

我有以下型号:

public class Input {
  public Dictionary<String, String> A { get; set; }
  public Dictionary<String, String> B { get; set; }
}

public class Output {
  public String Key { get; set; }
  public String A { get; set; }
  public String B { get; set; }
}

我从服务中得到了以下信息:

List<Input> inputs = new List<Input> {
  new Input {
    A = new Dictionary<String, Int32> {
      { "EN", "Aen" },
      { "FR", "Afr" }
    },
    B = new Dictionary<String, Int32> {
      { "EN", "Ben" },
      { "FR", "Bfr" }
    }
  }
  // More inputs ...
}

我需要将其映射到列表,如下所示:

List<Output> outputs = new List<Output> {
  new Output { Key = "EN", A = "Aen", B = "Ben" },
  new Output { Key = "FR", A = "Afr", B = "Bfr" }
}

我正在尝试以下操作:

var outputs = inputs
  .Select(input => input.A.Select(a => 
     new Output { Key = a.Key, A = a.Value ... })

问题是要创建输出,我需要A和B中的两个值。

5 个答案:

答案 0 :(得分:1)

您似乎可以在此处使用Join

var ouputs = input.A.Join(
    input.B,
    a => a.Key,
    b => b.Key,
    (a, b) => new Output { Key = a.Key, A = a.Value, B = b.Value }
);

从性能的角度来看,如果您已经拥有索引数据,则可以使用更多代码来改善这一点。

var ouputs = input.A.Map(a => {
    B b;
    if(input.B.TryGetValue(a.Key, out b)) {
        return { 
            Valid = true;
            Output = new Output { Key = a.Key, A = a.Value, B = b.Value }
        };
    } else {
        return { Valid = false, Output = null };
    }
}).Where(result => result.Valid)
  .Select(result => result.Output);

两者都是严格的内部联接。如果在没有对应关系的情况下需要A或B的数据,则情况会更加复杂。

答案 1 :(得分:0)

我假设AB包含相同的key集合。因此,input.A.Select将遍历A字典,并将为Key类中的AOutput提供值。对于B的值,我们可以直接使用input.B[a.Key]获取,这将减少进一步的联接或复杂的操作。

但是它将提供List<List<Output>>,所以我使用了inputs.SelectMany,它将所有值合并到单个List<Output>列表中。

var outputs = inputs.SelectMany(input => input.A.Select(
              a => new Output 
                   { 
                       Key = a.Key, 
                       A = a.Value, 
                       B = input.B[a.Key]
                   });

答案 2 :(得分:0)

在没有您提供更多信息的情况下,我假设A和B可能具有不同的键。
为了匹配预期的结果,我使用SelectMany来修饰列表输出的列表。

var outputs = inputs
                  .SelectMany(input =>
                    input
                      .A.Keys               // all key from A
                      .Union(input.B.Keys)  // + all key from B without the duplicate
                      .Select(k =>
                        new Output
                         {
                            Key = k,
                            A = input.A.GetValueOrDefault(k),
                            B = input.B.GetValueOrDefault(k)
                         }
                       ) 
                   );                      

使用这种引出方法,:

public static TValue GetValueOrDefault<TKey, TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default) =>
    dictionary.TryGetValue(key, out var ret) ? ret : defaultValue;

答案 3 :(得分:0)

我将假设您的词典可能具有不同的键

第一步-向您的Input类添加属性,以返回两个字典使用的不同字典键:

public class Input
{
    public Dictionary<string, string> A { get; set; }
    public Dictionary<string, string> B { get; set; }

    public IEnumerable<string> Keys
    {
        get
        {
            List<string> aKeys = A?.Keys.ToList() ?? new List<string>();
            List<string> bKeys = B?.Keys.ToList() ?? new List<string>();

            return aKeys.Union(bKeys);
        }
    }
}

第二步-创建将Input对象转换为IEnumerable<Output>的方法:

private static IEnumerable<Output> CreateOutput(Input input)
{
    foreach (string key in input.Keys)
    {
        string aValue = null;
        string bValue = null;

        input.A?.TryGetValue(key, out aValue);
        input.B?.TryGetValue(key, out bValue);

        yield return new Output
        {
            A = aValue,
            B = bValue,
            Key = key
        };
    }
}

最后,使用SelectMany将列表展平为一个列表:

List<Input> inputs = new List<Input>
{
    new Input
    {
        A = new Dictionary<string, string>
        {
            {"EN", "Aen"},
            {"FR", "Afr"},
            {"PT", "PT value"}, // key does not exist in B dictionary

        },
        B = new Dictionary<string, string>
        {
            {"EN", "Ben"},
            {"FR", "Bfr"},
            {"DE", "DE value"}, // key does not exist in A dictionary
        },
    }
};

List<Output> outputs = inputs.SelectMany(CreateOutput).ToList();

输出:

Output list

答案 4 :(得分:0)

因为您没有指定其他方式,所以我假设:

  • input.A可能具有与input.B相同的键,但是可能具有不在input.B中的键。
  • input.B可能具有与input.A相同的键,但是可能具有不在input.A中的键。
  • input.A和input.B都不为空(但可以为空)

    public List<Output> Flatten(Input input)
    {
        var outputs = new List<Output>();
        foreach (var aKey in input.A.Keys)
        {
            //Check that input.B really has the key found in input.A
            outputs.Add(new Output{Key = aKey, A = input.A[aKey], B = input.B.ContainsKey(aKey)? input.B[aKey]:null});
        }
        //If input.B has some keys not in input.A 
        foreach (var bKey in input.B.Keys)
        {
            //Check that the bKey was not already inserted (didn't exist earlier)
            if (outputs.All(x => x.Key != bKey))
            {
                //A can be null.. it was not in the input.A
                outputs.Add(new Output {Key = bKey, A = null, B = input.B[bKey]});
            }
        }
    
        return outputs;
    }
    

我会更好地将List重新定义为包含Key的字典以及包含字符串A和B的类,以便第二个循环更快。

相关问题