繁体   English   中英

如何确定哪个值在我的收藏中出现最多?

[英]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;

编辑

这个问题已经很老了,但是我要提到OrderByDescendingFirst件事是做多余的工作:您实际上不需要进行排序就可以得到最大的结果。 这是人们一直在做的一个古老的惰性黑客,因为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM