Home > database >  Select on empty list's group item gives non-empty list
Select on empty list's group item gives non-empty list

Time:03-16

This is my sample code:

var users = GetUsers();
var userGroups = users.GroupBy(x => x.Id);

foreach (var userGroup in userGroups)
{
    var user = userGroup.First();
    var carriedItems = userGroup.Select(x => x.CarriedItemId).ToList();
    Console.WriteLine($"UserId={user.Id}, CarriedItemsCount={carriedItems.Count}");
}

static IEnumerable<User> GetUsers()
{
    return new List<User>()
    {
        new User() { Id = 1, CarriedItemId = new List<int> { 6 }},
        new User() { Id = 1, CarriedItemId = new List<int> { 12 }},
        new User() { Id = 2, CarriedItemId = new List<int> { 2 }},
        new User() { Id = 3, CarriedItemId = new List<int>() }
    };
}

class User
{
    public int Id { get; set; }
    public List<int> CarriedItemId { get; set; }
}

Output of this is:

UserId=1, CarriedItemsCount=2
UserId=2, CarriedItemsCount=1
UserId=3, CarriedItemsCount=1

Why UserId=3 has 1 carried item, but not 0? After debugging I can see, that his carriedItems list contains default int32 value, which is 0. Why? I expected his carriedItems list to be empty. Can someone spot a bug?

CodePudding user response:

  var carriedItems = userGroup.Select(x => x.CarriedItemId).ToList();
  Console.WriteLine($"UserId={user.Id}, CarriedItemsCount={carriedItems.Count}");

carriedItems is not the count of CarriedItemId, its a list of each entry of the usergroup with that ID. The one for id = 3 has 1 entry that contains 0.

CodePudding user response:

You've messed up types and ranges. Your code as it is does not Count the things you think it does. Right now, it counts Collections. Not Items. See comments below:

class User
{
    public int Id { get; set; }

    // this 'CarriedItemId' is a LIST, so it's rather a "CarriedItemIds"
    public List<int> CarriedItemId { get; set; }
}


var users = GetUsers();
var userGroups = users.GroupBy(x => x.Id);

foreach (var userGroup in userGroups)
{
    var user = userGroup.First();

    // watch carefully what you select here
    // CarriedItemId is a LIST, not a single item
    // 'carriedItems' is a List<List<int>>
    var carriedItems = userGroup.Select(x => x.CarriedItemId).ToList();
    Console.WriteLine($"UserId={user.Id}, CarriedItemsCount={carriedItems.Count}");
}

Therefore, for a userId=3, there's one record, that record has an item list, which, although empty, is still a list, and record exists, so you get a 1.

To get a merged set of all itemIds, use SelectMany instead:

var carriedItems = userGroup.SelectMany(x => x.CarriedItemId).ToList();

This will merge all CarriedItemId from all User records, and you will end up with expected 2/1/0 counts.

  • Related