2015-08-14 1 views
0

Итак, у меня есть json-файл с списком фруктов. Фруктовый ключ может отображать один плод или коллекцию фруктов.Как определить, какое значение больше всего в моей коллекции?

например:

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

мне было интересно, как я могу определить, какой плод (ы) происходит больше всего в моей структуре JSon? То есть, как я узнаю, как часто происходит значение, а какое из них выше других?

+0

Этот JSON недействителен, согласно http://jsonlint.com/. Не могли бы вы обновить свой вопрос с помощью действительного JSON? – dbc

+0

в порядке выше json сейчас. – Euridice01

+0

@CodeCaster, я бы десериализовал с помощью XmlSerializer и, например, поставил вышеприведенный список. Итак, допустим, у меня есть список – Euridice01

ответ

4

Не уверен, что вы заинтересованы в том, чтобы класс был десериализован, но вот как вы это сделаете. Вы можете пропустить класс и использовать динамические десериализации:

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; 

EDIT:

Этот вопрос в довольно старый, но я упомяну, что OrderByDescending, First вещи делает лишнюю работу: вы не» t действительно нужно сортировать, чтобы получить максимум. Это вековой ленивый взлом, который люди продолжают делать, потому что LINQ не обеспечивает хороший метод расширения MaxBy.

Обычно ваш размер ввода достаточно мал, а другой материал добавляет достаточно накладных расходов, которые вам не очень-то нравятся, но «правильный» способ (например, если у вас были миллиарды видов фруктов) было бы использовать метод расширения proper MaxBy или взломать что-то из Aggregate. Поиск max является наихудшим линейным, тогда как сортировка - наихудший случай O(n log(n)).

+0

А это в значительной степени то, что мне нужно. Один вопрос: JsonConvert.DeserializeObject ... является частью Json.NET или Serializer? – Euridice01

+0

@ Euridice01 Это в JSON.Net. Вы можете установить его с NuGet. –

1

Если вы используете Json.NET, вы можете загрузить JSON с помощью LINQ to 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 
    } 
] 

** Обновление **

Забыла включить следующий метод расширения

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, а не десериализацию.

0

Класс сериализации для этой структуры проста:

public class RootObject 
{ 
    public List<List<string>> fruits { get; set; } 
} 

Так десериализовать:

var fruitListContainer = JsonConvert.DeserializeObject<RootObject>(jsonString); 

Затем вы можете поместить все фрукты в одном списке:

List<string> fruits = fruitListContainer.fruits.SelectMany(f => f); 

Теперь вам есть все фрукты в одном списке, и вы можете делать все, что хотите. Для сортировки см. Другие ответы.

0

Предполагая, что данные в файле с именем fruits.json, что JQ (http://stedolan.github.io/jq/) находится на пути, и что вы используете Mac или Linux стиле оболочки:

$ 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 
} 
Смежные вопросы