Flattern Dictionary <string,hashset <string =“”>&gt;

时间:2017-11-23 07:46:36

标签: c# dictionary hashset

我现在已经搞乱了几天了,我无法绕过它:

我有以下结构:

Dictionary<string, HashSet<string>>

包含以下数据:

P1     S1, S2
P2     S1, S2

P-prefixed值是字典键,S-prefixed值是HashSet值。

我需要的是以下输出列表,

P1, P2, S1, S2

如果我有以下起始值:

P1     S1, S2
P2     S1

输出列表应为:

P1, P2, S1, P1, S2

如果在几个字典项的HashSet中有类似的值,它应该将它们组合在一起,这就是为什么P1,P2有S1,P1只有S2

这是一个澄清更多的TestApp:

static void Main(string[] args)
{
    var start = new Dictionary<string, HashSet<string>>();
    var output = new List<string>();

    //example1
    start.Add("P1", new HashSet<string> { "S1", "S2" });
    start.Add("P2", new HashSet<string> { "S1", "S2" });
    output = HocusPocus(start);
    PrintResult(output); // should be P1, P2, S1, S2

    //example 2
    start.Clear();
    start.Add("P1", new HashSet<string> { "S1", "S2" });
    start.Add("P2", new HashSet<string> { "S1" });
    output = HocusPocus(start);
    PrintResult(output); // should be P1, P2, S1, P1, S2

    //example 3
    start.Clear();
    start.Add("P1", new HashSet<string> { "S1", "S2", "S3" });
    start.Add("P2", new HashSet<string> { "S1" });
    start.Add("P3", new HashSet<string> { "S1", "S2" });
    output = HocusPocus(start);
    PrintResult(output); // should be P1, P2, P3, S1, P1, P3, S2, P1, S3

    Console.ReadKey();
}

public static List<string> HocusPocus(Dictionary<string, HashSet<string>> data)
{
    // magic happens here
}

public static void PrintResult(List<string> result)
{
    result.ForEach(x => Console.Write($"{x},"));
    Console.WriteLine();
}

2 个答案:

答案 0 :(得分:3)

对于每个S,您似乎想要找到与该S相关联的所有P,并且还使用相同的P值组来分组S值。如果您的数据集不是很大,那么即使不是以最有效的方式,也可以非常简单地完成。

您的数据:

var start = new Dictionary<string, HashSet<string>> {
    { "P1", new HashSet<string> { "S1", "S2", "S3" } },
    { "P2", new HashSet<string> { "S1" } },
    { "P3", new HashSet<string> { "S1", "S2" } }
};

首先,下一次迭代需要所有的S值:

var allSValues = start.SelectMany(kvp => kvp.Value).Distinct();

为了能够按P值序列进行分组,需要IEqualityComparer<IEnumerable<string>>

class SequenceEqualityComparer : IEqualityComparer<IEnumerable<string>>
{
    // Does not handle null values correctly.
    public bool Equals(IEnumerable<string> x, IEnumerable<string> y) => x.SequenceEqual(y);

    public int GetHashCode(IEnumerable<string> obj)
    {
        unchecked {
            return obj.Aggregate(17, (hash, @string) => hash * 23*@string.GetHashCode());
        }
    }
}

(比较应该将输入视为集合,而不是序列,但为此目的,排序是稳定的,因此它将起作用。)

找到每个S值的P值,然后将其分组为一个类似于字典的查找,除了每个键(P值序列)可以有多个值(S值):

var lookup = allSValues.Select(s => new
    {
        S = s,
        PValues = start
            .Where(kvp => kvp.Value.Contains(s))
            .Select(kvp => kvp.Key)
            .ToList()
    })
    .ToLookup(item => item.PValues, item => item.S, new SequenceEqualityComparer());

您可以打印查找:

foreach (var items in lookup)
{
    Console.Write(string.Join(" ", items.Key));
    Console.Write(" ");
    Console.WriteLine(string.Join(" ", items));
}

答案 1 :(得分:1)

这是Martin's的一个版本,用于遍历主要字典以构建反向字典,并使用集合而不是序列(它也适用于原始方法签名):

home1/ja22/public_html/exam