如何确定集合中出现次数最多的值

本文关键字:何确定 集合 | 更新日期: 2023-09-27 18:07:57

所以,我有一个json文件,其中有一个水果列表。水果键可以映射到单个水果或水果集合。

E。g:

[
    {
        "fruits": [
            "banana"
        ]
    },
    {
        "fruits": [
            "apple"
        ]
    },
    {
        "fruits": [
            "orange",
            "apple"
        ]
    }
]

我想知道,我怎么能确定哪些水果(s)发生在我的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,然后使用SelectTokens递归地找到所有"fruits"属性,然后递归地收集所有后代字符串值(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
  }
]

** Update **

忘记包含以下扩展方法

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的文件中。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
}