[英]How can I determine which value occurs the most in my collection?
因此,我有一个包含水果列表的json文件。 水果键可以映射到单个水果或水果集合。
例如:
[
{
"fruits": [
"banana"
]
},
{
"fruits": [
"apple"
]
},
{
"fruits": [
"orange",
"apple"
]
}
]
我想知道如何确定json结构中出现最多的水果? 也就是说,我怎么知道我的价值出现的频率以及哪个领先于另一个?
不知道您是否有兴趣对反序列化的类感兴趣,但是这是您的方法。 随意跳过该类并使用动态反序列化:
class FruitCollection
{
string[] Fruits { get; set; }
}
var fruitColls = JsonConvert.DeserializeObject<FruitCollection>(json);
var mostCommon = fruitColls
.SelectMany(fc => fc.Fruits)
.GroupBy(f => f)
.OrderByDescending(g => g.Count())
.First()
.Key;
编辑 :
这个问题已经很老了,但是我要提到OrderByDescending
, First
件事是做多余的工作:您实际上不需要进行排序就可以得到最大的结果。 这是人们一直在做的一个古老的惰性黑客,因为LINQ没有提供一种很好的MaxBy
扩展方法。
通常,您的输入大小足够小,而其他内容则增加了您并不真正在乎的开销,但是“正确”的方式(例如,如果您有数十亿种水果类型)将是使用适当的MaxBy扩展方法或修改某些内容不Aggregate
。 找到最大值是最坏情况的线性,而排序是最坏情况O(n log(n))
。
如果您使用Json.NET ,则可以使用LINQ到JSON来加载json,然后使用SelectTokens
递归查找所有"fruits"
属性,然后递归收集所有后代字符串值( JValue
类型的JValue
), JValue
其字符串值分组,并按降序排列:
var token = JToken.Parse(jsonString);
var fruits = token.SelectTokens("..fruits") // Recursively find all "fruit" properties
.SelectMany(f => f.DescendantsAndSelf()) // Recursively find all string literals undernearh each
.OfType<JValue>()
.GroupBy(f => (string)f) // Group by string value
.OrderByDescending(g => g.Count()) // Descending order by count.
.ToList();
或者,如果您想将结果放入匿名类型中以求清楚,请执行以下操作:
var fruits = token.SelectTokens("..fruits") // Recursively find all "fruit" properties
.SelectMany(f => f.DescendantsAndSelf()) // Recursively find all string literals undernearh each
.OfType<JValue>()
.GroupBy(f => (string)f) // Group by string value
.Select(g => new { Fruit = (string)g.Key, Count = g.Count() } )
.OrderByDescending(f => f.Count) // Descending order by count.
.ToList();
然后,然后:
Console.WriteLine(JsonConvert.SerializeObject(fruits, Formatting.Indented));
生产:
[ { "Fruit": "apple", "Count": 2 }, { "Fruit": "banana", "Count": 1 }, { "Fruit": "orange", "Count": 1 } ]
**更新**
忘记包含以下扩展方法
public static class JsonExtensions
{
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new [] { node };
}
}
最初的问题在JSON的精确结构上有点模糊,这就是为什么我建议使用Linq而不是反序列化。
此结构的序列化类很简单:
public class RootObject
{
public List<List<string>> fruits { get; set; }
}
所以要反序列化:
var fruitListContainer = JsonConvert.DeserializeObject<RootObject>(jsonString);
然后,您可以将所有水果放在一个列表中:
List<string> fruits = fruitListContainer.fruits.SelectMany(f => f);
现在,所有水果都列在一个列表中,您可以做任何您想做的事情。 有关排序,请参见其他答案。
假设数据在一个名为fruits.json的文件中,则jq( http://stedolan.github.io/jq/ )在PATH上,并且您使用的是Mac或Linux风格的shell:
$ jq 'reduce (.[].fruits[]) as $fruit ({}; .[$fruit] += 1)' fruits.json
{
"banana": 1,
"apple": 2,
"orange": 1
}
在Windows上,如果对引号进行了适当的调整,同样的事情将起作用。 或者,如果将单行jq程序放在文件中,例如Fruits.jq,则可以在任何受支持的环境中运行以下命令:
jq -f fruits.jq fruits.json
如果数据来自其他进程,则可以将其通过管道传输到jq中,例如:
jq -f fruits.jq
查找最大计数的一种方法是添加几个过滤器,例如,如下所示:
$ jq 'reduce (.[].fruits[]) as $fruit ({}; .[$fruit] += 1) |
to_entries | max_by(.value)' fruits.json
{
"key": "apple",
"value": 2
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.