0

I am learning LINQ in C#. I have the following JSON data:

[
    {
        "product": [
                   "a", "b", "c"
                  ]
    },
    {
        "product": [
                  "a","b"
                  ]
    },
    {
        "product": [
                   "b","c"
                 ]
    },
    {
       "product": [
                   "b", "c"
                ]
    },
    {
        "product": [
                  "a","b"
                  ]
    },
    {
       "product": [
                   "b", "c"
                ]
    },
    {
        "product": [
                  "a"
                  ]
    },
]

I would like to perform a LINQ by grouping product over its count then order by descending and finally selecting the top 3.

How can I do that? In the result, I have to show the product and its count like below:

 "b","c"            3
 "a","b"            2 
 "a","b","c"        1

My current code is:

public class Product
{
    public List<string> Name { get; set; }
}

string json = File.ReadAllText("products.json");
var products = JsonConvert.DeserializeObject<List<Product>>(json);
var result = (from p in products
              .GroupBy(pt => pt.Name)
              .OrderByDescending(pt => pt.Count())
              .SelectMany(pt => pt) 
              select p).Take(3);

But I am getting all 1 as count. Can you please help to correct the result? Thank you.

Matheus Lacerda
  • 5,983
  • 11
  • 29
  • 45
aliceangel
  • 304
  • 1
  • 5
  • 19

1 Answers1

0

You are trying to group by a sequence but without custom comparator. That will not work.
To achive your desired output try do this:

Define your custom equality comparer:

public class SequenceComparer : IEqualityComparer<string[]>
{
    public bool Equals(string[] x, string[] y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        return x.SequenceEqual(y);
    }

    public int GetHashCode(string[] obj)
    {
        return obj.Aggregate(42, (c, n) => c ^ n.GetHashCode());
    }
}

Parse your json to anonymous object

var json = "[\r\n    {\r\n        \"product\": [\r\n                   \"a\", \"b\", \"c\"\r\n                  ]\r\n    },\r\n    {\r\n        \"product\": [\r\n                  \"a\",\"b\"\r\n                  ]\r\n    },\r\n    {\r\n        \"product\": [\r\n                   \"b\",\"c\"\r\n                 ]\r\n    },\r\n    {\r\n       \"product\": [\r\n                   \"b\", \"c\"\r\n                ]\r\n    },\r\n    {\r\n        \"product\": [\r\n                  \"a\",\"b\"\r\n                  ]\r\n    },\r\n    {\r\n       \"product\": [\r\n                   \"b\", \"c\"\r\n                ]\r\n    },\r\n    {\r\n        \"product\": [\r\n                  \"a\"\r\n                  ]\r\n    },\r\n]";

var products = JsonConvert.DeserializeAnonymousType(json,
  new[] {new {product = new string[] { }}});

Group by product with your custom equality comparer

var result = products.GroupBy(p => p.product, new SequenceComparer())
    .Select(g => new {g.Key, Count = g.Count()})
    .OrderByDescending(x => x.Count)
    .Take(3);

Now you can see a result:

foreach (var row in result)
    Console.WriteLine($"{string.Join(",", row.Key)} {row.Count}");

will output

'b,c' 3
'a,b' 2
'a,b,c' 1

Live demo

Aleks Andreev
  • 7,016
  • 8
  • 29
  • 37
  • Thank you very much. It worked. :) If you have time, could you please tell me why we need the hash function and how did you calculate the hashcode? – aliceangel Mar 19 '18 at 20:29
  • @shahnaj [This](https://stackoverflow.com/q/371328/4685428) question may help you to understand why `GetHashCode` is important. And [this](https://stackoverflow.com/q/2730865/4685428) question may guide you to "how to calculate" – Aleks Andreev Mar 19 '18 at 21:27