【问题标题】:LINQ to JSON group query on arrayLINQ to JSON 数组查询
【发布时间】:2020-01-04 01:11:56
【问题描述】:

我有一个 JSON 数据示例,我正在使用 NewtonSoft 将其转换为 JArray。

        string jsonString = @"[{'features': ['sunroof','mag wheels']},{'features': ['sunroof']},{'features': ['mag wheels']},{'features': ['sunroof','mag wheels','spoiler']},{'features': ['sunroof','spoiler']},{'features': ['sunroof','mag wheels']},{'features': ['spoiler']}]";

我正在尝试检索最常一起请求的功能。基于上述数据集,我的预期输出将是:

天窗,磁轮,2
天窗, 1
磁轮 1
天窗、磁轮、扰流板、1
天窗,扰流板,1
扰流板,1

但是,我的 LINQ 生锈了,我用来查询 JSON 数据的代码返回的是单个功能的计数,而不是一起选择的功能:

        JArray autoFeatures = JArray.Parse(jsonString);
        var features = from f in autoFeatures.Select(feat => feat["features"]).Values<string>()
                       group f by f into grp
                       orderby grp.Count() descending
                       select new { indFeature = grp.Key, count = grp.Count() };

        foreach (var feature in features)
        {
            Console.WriteLine("{0}, {1}", feature.indFeature, feature.count);
        }

实际输出:
天窗,5
磁轮,4
扰流板,3

我在想也许我的查询需要一个“不同的”,但我不确定。

【问题讨论】:

  • var features = JsonConvert.DeserializeObject&lt;List&lt;Dictionary&lt;string, string[]&gt;&gt;&gt;(jsonString).SelectMany(d =&gt; d).GroupBy(k =&gt; string.Concat(k.Value.OrderBy(s =&gt; s))).Select(g =&gt; new { Feature = g.Key, Count = g.Count() }).OrderByDescending(a =&gt; a.Count);。值字符串在内部预先排序(以生成忽略字符串值位置的有序组)

标签: c# json linq


【解决方案1】:

这是 Select 的问题。您告诉它让在数组中找到的每个值都成为它自己的项目。实际上,您需要将所有值组合成每个特征的字符串。这是你的做法

var features = from f in autoFeatures.Select(feat => string.Join(",",feat["features"].Values<string>()))
                       group f by f into grp
                       orderby grp.Count() descending
                       select new { indFeature = grp.Key, count = grp.Count() };

产生以下输出

sunroof,mag wheels, 2
sunroof, 1
mag wheels, 1
sunroof,mag wheels,spoiler, 1
sunroof,spoiler, 1
spoiler, 1

【讨论】:

  • 这正是我所需要的。谢谢。我没有意识到我必须将这些字符串连接在一起。我想有一种方法可以在不进行字符串操作的情况下提取该信息。
  • 使用 LINQ 很可能有办法做到这一点,但它远远超出了我的能力范围!虽然希望现在我已经发布了答案,但专家们会站出来展示我的版本是多么错误和低效,我们都可以学到一些东西:)
【解决方案2】:

您可以使用HashSet 来识别不同的功能集,并对这些集进行分组。这样一来,您的 Linq 看起来与您现在拥有的基本相同,但您需要在 GroupBy 中添加一个 IEqualityComparer 类来帮助将一组功能与另一组功能进行比较,以检查它们是否相同。

例如:

var featureSets = autoFeatures
    .Select(feature => new HashSet<string>(feature["features"].Values<string>()))
    .GroupBy(a => a, new HashSetComparer<string>())
    .Select(a => new { Set = a.Key, Count = a.Count() })
    .OrderByDescending(a => a.Count);

foreach (var result in featureSets)
{
    Console.WriteLine($"{String.Join(",", result.Set)}: {result.Count}");
}

比较器类利用HashSet 类的SetEquals 方法来检查一组是否与另一组相同(这会处理字符串在组中的不同顺序等)

public class HashSetComparer<T> : IEqualityComparer<HashSet<T>>
{
    public bool Equals(HashSet<T> x, HashSet<T> y)
    {
        // so if x and y both contain "sunroof" only, this is true 
        // even if x and y are a different instance
        return x.SetEquals(y);
    }

    public int GetHashCode(HashSet<T> obj)
    {
        // force comparison every time by always returning the same, 
        // or we could do something smarter like hash the contents
        return 0; 
    }
}

【讨论】:

  • 刚刚在更复杂的数据集上尝试了您的解决方案。 HashSetComparer 对于捕获和组合(分组)功能未按相同顺序列出的情况至关重要。谢谢。
  • 当然,这是一个很好的考虑,但我不太确定这是对这个问题的一个很好的答案。因为,似乎提出这种考虑的好处在于它没有成为问题本身的一部分。此外,提出这一考虑会引发更多考虑:这些功能的开始顺序和/或这些功能开始的顺序是否有任何意义。如果您可能希望“天窗,磁轮”和“磁轮,天窗”难以区分,那么您不会使用这种方法;如果您已经可以订购features,则不需要这种方法。
  • 我不想过多剖析这个深思熟虑的答案,但如果有人认为这是最好的方法......这取决于。
  • 根据我的问题“按要求”,这有点矫枉过正。根据我的实际需求和数据集,这个答案是绝对有必要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多