C # / Linq - Группировка по 2-му элементу, возвращаемому в строковом массиве, в то время как родитель остается - PullRequest
2 голосов
/ 13 января 2020

У меня есть следующие данные, возвращенные из API, и я пытаюсь использовать linq для изменения формы данных. Реально ли это сделать и получить ожидаемый результат ниже.

  "results": [
    {
      "Description": "Describe1",
      "Cost": 5.00,
      "Category": [
        "Online",
        "Games"
      ]
    },
    {
      "Description": "Describe2",
      "Cost": 4.00,
      "Category": [
        "Online",
        "Games"
      ]
    },
    {
      "Description": "Describe3",
      "Cost": 3.00,
      "Category": [
        "Online",
        "Grocery"
      ]
    },
    {
      "Description": "Describe4",
      "Cost": 3.00,
      "Category": [
        "Transport",
        "Bus"
      ]
    },
    {
      "Description": "Describe5",
      "Cost": 3.00,
      "Category": [
        "Transport",
        "Bus"
      ]
    },
    {
      "Description": "Describe5",
      "Cost": 10.00,
      "Category": [
        "Transport",
        "Train"
      ]
    }
}

Окончательный результат, который я пытаюсь достичь из вышеперечисленного:

{ name : "Online", 
    data: [
    {
        name: Games,
        value: 9.00
    },
    {
        name : Grocery,
        value: 3.00
    }],
 name : "Transport",
 data: [
    {
        name: Bus,
        value: 6.00
    },
    {
        name : Train,
        value: 10.00
    }],
}

Группировать по FirstOrDefault очень просто достаточно для статистики первого уровня, но я не вижу, с чего начать подгруппу!

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 13 января 2020

Это должно работать:

class ResultItem {
    public string Name { get; set; }
    public double Value { get; set; }
}

class ResultGroup {
    public string Name { get; set; }
    public ResultItem[] Data { get; set; }
}

var results = items
    .GroupBy(x => x.Category[0])
    .Select(g => new ResultGroup {
        Name = g.Key,
        Data = g
            .GroupBy(x => x.Category[1])
            .Select(g2 => new ResultItem { Name = g2.Key, Value = g2.Sum(x => x.Cost) })
            .ToArray()
        });
0 голосов
/ 13 января 2020

Отвечая на ваш вопрос в двух частях,

Часть 1

Первая часть состоит из нескольких шагов

  • Десериализация существующего Json в пользовательские структуры данных
  • Изменение и создание новых структур данных

При использовании Linq можно выполнить следующие шаги:

var parsedData = JsonConvert.DeserializeObject<RootObject>(str).Results.GroupBy(x=> x.Category.First())
        .Select(x=>
            new {
                 name= x.Key,
                 data =  x.GroupBy(c=>c.Category.Last())
                           .Select(c=> new {value=c.Sum(f=>f.Cost),name=c.Key})
                });

Где RootObject определяется как

public class Result
{
    public string Description { get; set; }
    public double Cost { get; set; }
    public List<string> Category { get; set; }
}

public class RootObject
{
    [JsonProperty("results")]
    public List<Result> Results { get; set; }
}


Часть 2

Вторая часть должна быть выполнена в зависимости от того, как вы хотите, чтобы Json был отформатирован. Если вы наблюдаете ожидаемый результат, указанный в OP, это не похоже на действительный Json. Упрощая его для наглядности, строка Json выглядит следующим образом

{ 
 name : "Online", 
 data: [....],
 name : "Transport",
 data: [...],
}

Как уже отмечалось, поля имени и данных дублируются в пределах json. Если вы хотите получить результат именно таким образом, вам нужно сериализовать Json, полученный в Part 1 , и изменить его, используя манипуляции со строками. Например,

var jsonCollection =  parsedData.Select(x=> Regex.Replace(JsonConvert.SerializeObject(x,Newtonsoft.Json.Formatting.Indented),@"^{|}$",string.Empty));
var finalResult = $"{{{string.Join(",",jsonCollection)}}}";

Выход

{
  "name": "Online",
  "data": [
    {
      "value": 9.0,
      "name": "Games"
    },
    {
      "value": 3.0,
      "name": "Grocery"
    }
  ]
,
  "name": "Transport",
  "data": [
    {
      "value": 6.0,
      "name": "Bus"
    },
    {
      "value": 10.0,
      "name": "Train"
    }
  ]
}

Если желаемый результат должен быть действительным json, то вы можете убедиться, что это массив. Например,

[
{ 
 name : "Online", 
 data: [....]
},
{
 name : "Transport",
 data: [...],
}
]

Затем вы можете сериализовать результат, который вы получили в Part 1 напрямую.

var finalResult = JsonConvert.SerializeObject(parsedData, Newtonsoft.Json.Formatting.Indented);

Выход

[
  {
    "name": "Online",
    "data": [
      {
        "value": 9.0,
        "name": "Games"
      },
      {
        "value": 3.0,
        "name": "Grocery"
      }
    ]
  },
  {
    "name": "Transport",
    "data": [
      {
        "value": 6.0,
        "name": "Bus"
      },
      {
        "value": 10.0,
        "name": "Train"
      }
    ]
  }
]

Демонстрационный код

0 голосов
/ 13 января 2020

Вы можете достичь этого простым способом, Live демо здесь

        var parsedJsonObject = JsonConvert.DeserializeObject<List<ObjectName>>(jsonObject);
        var normalizedData = parsedJsonObject.SelectMany(pParent => pParent.Category, (pParent, pCategory) => 
                                                new { pParent, pCategory }).Select(ParentAndCategory =>
                                                                                new
                                                                                {
                                                                                    Cost = ParentAndCategory.pParent.Cost,
                                                                                    Category = ParentAndCategory.pCategory,
                                                                                }).ToList();
        var aggregatedData = new List<ObjectName2>(); 

        for(int i = 0; i < (normalizedData.Count - 1);)
        {
            aggregatedData.Add(new ObjectName2{ Cost = normalizedData[i].Cost, Category1 = normalizedData[i].Category, Category2 = normalizedData[i + 1].Category });
            i += 2;
        }

        var result = aggregatedData.GroupBy(p => p.Category1)
                                    .Select(g => new 
                                            { 
                                                name = g.Key, 
                                                data = g.GroupBy(p => p.Category2).Select(g2 => 
                                                                                          new { name = g2.Key, value = g2.Sum(p2 => p2.Cost) })
                                            }).ToList();
        foreach(var item in result)
            Console.WriteLine(JsonConvert.SerializeObject(item));

Вывод

{"name":"Online","data":[{"name":"Games","value":9.00},{"name":"Grocery","value":3.00}]}

{"name":"Transport","data":[{"name":"Bus","value":6.00},{"name":"Train","value":10.00}]}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...