LINQ to JSON 数组查询
Posted
技术标签:
【中文标题】LINQ to JSON 数组查询【英文标题】:LINQ to JSON group query on array 【发布时间】: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<List<Dictionary<string, string[]>>>(jsonString).SelectMany(d => d).GroupBy(k => string.Concat(k.Value.OrderBy(s => s))).Select(g => new Feature = g.Key, Count = g.Count() ).OrderByDescending(a => a.Count);
。值字符串在内部预先排序(以生成忽略字符串值位置的有序组)
【参考方案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
,则不需要这种方法。
我不想过多剖析这个深思熟虑的答案,但如果有人认为这是最好的方法......这取决于。
根据我的问题“按要求”,这有点矫枉过正。根据我的实际需求和数据集,这个答案是绝对有必要的。以上是关于LINQ to JSON 数组查询的主要内容,如果未能解决你的问题,请参考以下文章