Usando o Linq para agrupar uma lista de objetos em uma nova lista agrupada de lista de objetos


149

Eu não sei se isso é possível no Linq, mas aqui vai ...

Eu tenho um objeto:

public class User
{
  public int UserID { get; set; }
  public string UserName { get; set; }
  public int GroupID { get; set; }
}

Devolvo uma lista que pode parecer com a seguinte:

List<User> userList = new List<User>();
userList.Add( new User { UserID = 1, UserName = "UserOne", GroupID = 1 } );
userList.Add( new User { UserID = 2, UserName = "UserTwo", GroupID = 1 } );
userList.Add( new User { UserID = 3, UserName = "UserThree", GroupID = 2 } );
userList.Add( new User { UserID = 4, UserName = "UserFour", GroupID = 1 } );
userList.Add( new User { UserID = 5, UserName = "UserFive", GroupID = 3 } );
userList.Add( new User { UserID = 6, UserName = "UserSix", GroupID = 3 } );

Quero poder executar uma consulta Linq na lista acima que agrupa todos os usuários por GroupID. Portanto, a saída será uma lista de listas de usuários que contêm usuários (se isso faz sentido?). Algo como:

GroupedUserList
    UserList
        UserID = 1, UserName = "UserOne", GroupID = 1
        UserID = 2, UserName = "UserTwo", GroupID = 1
        UserID = 4, UserName = "UserFour", GroupID = 1
    UserList
        UserID = 3, UserName = "UserThree", GroupID = 2
    UserList
        UserID = 5, UserName = "UserFive", GroupID = 3
        UserID = 6, UserName = "UserSix", GroupID = 3

Eu tentei usar a cláusula groupby linq, mas isso parece retornar uma lista de chaves e não está agrupada corretamente:

var groupedCustomerList = userList.GroupBy( u => u.GroupID ).ToList();

Respostas:


309
var groupedCustomerList = userList
    .GroupBy(u => u.GroupID)
    .Select(grp => grp.ToList())
    .ToList();

1
muito legal, exatamente o que eu precisava. obrigado. fazendo isso da maneira imperativa é uma merda.
user1841243

1
Está funcionando bem? porque eu sou usado dessa maneira não funcionou. objModel.tblDonars.GroupBy (t => novo {t.CreatedOn.Year, t.CreatedOn.Month, t.CreatedOn.Day}). Selecione (g => novo {tblDonar = g.ToList ()}). ToList ( ); isso não está funcionando ... você pode ajuda ....
Rajamohan Anguchamy

com a sua solução, está funcionando bem, mas suponho que uma pergunta no grupo tenha até 8 valores e eu quero apenas precisar em todos os grupos com apenas 6 tomadas, então como fazer isso, por favor me avise.
coderwill

existe uma maneira de listar nomes de usuário e IDs de usuário separadamente depois de agrupar por ID de grupo? como - {"1", [1, 2, 4], ["UserOne", "UserTwo", "UserFour"]} para o groupID 1. #
Ajay Aradhya

1
Atualmente, o código não funciona e causa um erro se você tentar convertê-lo no tipo de lista anterior. Como retornar uma lista em select cria uma lista dentro de uma lista que não é a saída desejada aqui. Para quem tem problemas, posso sugerir: var groupedCustomerList = userList.GroupBy (u => u.GroupID). Selecione (grp => grp.First ()). ToList ();
aliassce

36

Seu grupo declaração vontade grupo, ID de grupo. Por exemplo, se você escrever:

foreach (var group in groupedCustomerList)
{
    Console.WriteLine("Group {0}", group.Key);
    foreach (var user in group)
    {
        Console.WriteLine("  {0}", user.UserName);
    }
}

isso deve funcionar bem. Cada grupo tem uma chave, mas também contém umaIGrouping<TKey, TElement> que é uma coleção que permite iterar sobre os membros do grupo. Como Lee menciona, você pode converter cada grupo em uma lista, se realmente quiser, mas se você quiser iterar sobre eles conforme o código acima, não há benefício real em fazê-lo.


4

Para o tipo

public class KeyValue
{
    public string KeyCol { get; set; }
    public string ValueCol { get; set; }
}

coleção

var wordList = new Model.DTO.KeyValue[] {
    new Model.DTO.KeyValue {KeyCol="key1", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key2", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key3", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key4", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key5", ValueCol="value3" },
    new Model.DTO.KeyValue {KeyCol="key6", ValueCol="value4" }
};

nossa consulta linq se parece abaixo

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList() };

ou para matriz em vez de lista como abaixo

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList().ToArray<string>() };

-1

Ainda era antigo, mas a resposta de Lee não me deu o grupo. Portanto, estou usando a seguinte instrução para agrupar uma lista e retornar uma lista agrupada:

public IOrderedEnumerable<IGrouping<string, User>> groupedCustomerList;

groupedCustomerList =
        from User in userList
        group User by User.GroupID into newGroup
        orderby newGroup.Key
        select newGroup;

Cada grupo agora tem uma chave, mas também contém um IGrouping, que é uma coleção que permite que você itere sobre os membros do grupo.


Isso responde à sua pergunta, não à pergunta do OP. Eles querem apenas uma lista de listas. Seu código não fornece isso.
GER Arnold #

Publiquei esta solução porque a resposta aceita acima do @Lee não funciona ao iterar, pois não fornece o group.keyelemento ao iterar na lista agrupada, como mostrado na resposta do @JohnSkeet. Minha resposta fornecido faz fornecer o elemento group.key quando a iteração. Portanto, pode ajudar outros a cair na armadilha que as respostas fornecidas não funcionem como mostrado acima. Portanto, é apenas mais uma maneira de obter a lista com o benefício de obter o elemento group.key semelhante à resposta aceita. Não entenda sua reivindicação @GertArnold. (.net core 3.0)
datapool 13/11/19

Eu acho que toda a questão se baseia na falta de compreensão do que IGroupingé. É por isso que o OP descarta seu próprio código correto na parte inferior, que é essencialmente idêntico ao seu código. É por isso que eles aceitam uma resposta que fornece exatamente o que pedem. O que eles precisam é de uma explicação, que JS forneceu suficientemente.
Gert Arnold
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.