【问题标题】:Flattern Dictionary<string, HashSet<string>>Flattern Dictionary<string, HashSet<string>>
【发布时间】:2018-05-07 02:23:00
【问题描述】:

我已经搞砸了这几天了,我无法理解它:

我有以下结构:

Dictionary<string, HashSet<string>>

包含以下数据:

P1     S1, S2
P2     S1, S2

其中 P 前缀值是字典键,S 前缀值是 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();
}

【问题讨论】:

  • 你能更明确地说你真正想要发生的事情吗?我可以尝试从您的输出中猜测,但它不是很清楚。什么是 P 和 S?
  • 当您创建一个字符串哈希集时,您通常会使用不属于普通数据的字符进行合并,例如“P1^P2^S1^P1^S2”。
  • 你尝试过什么吗?我们为什么要做你的工作?构建一个循环来迭代您的字典并将值放入扁平列表中并不难。无论如何,输出应该是P1, P2, S1, S2P1, P2, S1, P1, S2
  • 当然,我已经尝试了一些东西,比如循环和其他东西,但这只能让我到目前为止。它是收集字典键、比较 HashSet 值、相交它们的组合......

标签: c# dictionary hashset


【解决方案1】:

您似乎希望为每个 S 找到与该 S 相关联的所有 P,并进一步将 S 值与同一组 P 值进行分组。如果您的数据集不是很大,即使不是以最有效的方式,您也可以很简单地做到这一点。

您的数据:

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&lt;IEnumerable&lt;string&gt;&gt;

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));
}

【讨论】:

  • 这太棒了!您的代码适用于示例 2 和 3,但实际上不适用于 example1,它返回 P1,P2,S1,P1,P2,S2 我期望 P1,P2,S1,S2 的地方 - 但也许我可以解决这个问题
  • @MichaelBruyninckx 可能很棒,但它并没有达到您对预期输出的暗示...因为您需要识别共享 P 值的 S 值并将它们联合在一起。跨度>
  • @MichaelBruyninckx:你和亚当是对的,我忽略了你问题的一个重要方面。我已经相应地更新了我的答案。
  • @MartinLiversage 好朋友,我自己就是这么做的。我会给你投票而不是发布类似的答案;-)
  • @MartinLiversage 非常感谢!我猜它有点超出我的技能,过去 3 天我一直在以其他方式鬼混,所以这真的帮助了我!谢谢!
【解决方案2】:

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

public static List<string> HocusPocus(Dictionary<string, HashSet<string>> data)
{
    var invert = new Dictionary<string, HashSet<string>>();

    foreach (var kvp in data)
    {
        foreach (var s in kvp.Value)
        {
            if (!invert.TryGetValue(s, out var pvalues))
            {
                pvalues = new HashSet<string>();
                invert[s] = pvalues;
            }

            pvalues.Add(kvp.Key);
        }
    }

    var lookup = invert
        .ToLookup(_ => _.Value, _ => _.Key, new SetComparer());

    var flat = new List<string>();

    foreach (var item in lookup)
    {
        flat.AddRange(item.Key);
        flat.AddRange(item);
    }

    return flat;
}


public class SetComparer : IEqualityComparer<ISet<string>>
{
    public bool Equals(ISet<string> x, ISet<string> y)
    {
        return x.SetEquals(y);
    }

    public int GetHashCode(ISet<string> obj)
    {
        unchecked
        {
            int hash = 19;
            foreach (var foo in obj)
            {
                hash = hash * 31 + foo.GetHashCode();
            }
            return hash;
        }
    }
}

【讨论】:

  • 此解决方案具有更好的性能,因为它不需要单独的传递来收集 S 值。
猜你喜欢
  • 1970-01-01
  • 2012-09-21
  • 2012-03-01
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
  • 1970-01-01
相关资源
最近更新 更多