LINQ Group By 和合并属性
LINQ Group By and merge properties
private static void Main()
{
var accounts = new List<Account>()
{
new Account { PrimaryId = 1, SecondaryId = 10 },
new Account { PrimaryId = 1, SecondaryId = 12 }
};
}
public class Account
{
public int PrimaryId { get; set; }
public IList<int> SecondaryIds { get; set; }
public int SecondaryId { get; set; }
}
假设我有上面的代码,我想按PrimaryId分组,然后将SecondaryId合并到SecondaryIds中。
例如:
// PrimaryId = 1
// SecondaryIds = 10, 12
如何实现?
到目前为止我已经这样做了
var rs = accounts.GroupBy(g => g.PrimaryId == 1)
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
当使用 .GroupBy()
后跟 .Select()
时,您需要在分组项目中向下钻取一层:
var rs = accounts
.GroupBy(a => a.PrimaryId)
.Select(gr => new Account {
PrimaryId = gr.Key,
SecondaryIds = gr.Select(account => account.SecondaryId).ToList()
});
也许我会做什么
LINQ GroupBy
以其最简单的形式采用 lambda,returns 作为分组依据的键。 .GroupBy(a => a.PrimaryId)
。它产生类似于“列表列表”的东西,其中内部列表中的所有内容都具有相同的键。
如果你做了那个分组,你可以这样探索它:
var listOfLists = accounts.GroupBy(a => a.PrimaryId);
foreach(var innerList in listOfLists){
Console.WriteLine("Key is: " + innerList.Key);
foreach(Account account in innerList)
Console.WriteLine("Sid is: " + account.SecondaryId);
}
所以它有效地将您的帐户对象放入 List<List<Account>>
之类的东西中,除了内部列表有一个 Key
属性 告诉您帐户的共同价值(PrimaryId)在内部列表中全部共享
GroupBy 有一个扩展形式,它接受第二个参数“你想将什么项目(可能来自原始对象)放入内部列表?”,我们可以这样使用它:
.GroupBy(a => a.PrimaryId, a => a.SecondaryId)
这会创建类似 List<List<int>>
的内容,因为它不是整个帐户对象,而是将 SecondaryId 拉出并将 that 放入内部列表。这很好,因为您仍然可以从 Key
中获取 PrimaryId,并且 Account
中没有任何其他内容
实际上还有另一种形式的 GroupBy,带有 第三个 参数,它将获取结果“列表列表”中的每个项目,并允许您使用 那个..
.GroupBy(
a => a.PrimaryId, //derive the key
a => a.SecondaryId, //derive the innerList
(key, innerList) => new Account { PrimaryId = key, SecondaryIds = innerList.ToList() }
)
您可以将第三个参数想象成您放在 groupby 末尾的 .Select()
。它略有不同,并且可能更容易使用,因为“键”和“具有该键的事物列表”之间的明确区分
所以,第一个参数 (key,
是分组键(PrimaryID
),第二个参数 innerList)
是所有 SecondaryId
的列表那个分组键。我们可以使用这两位信息来创建一个新的 Account
对象,其中设置了 Primary 和 SecondaryIds
我在评论中提到,在一个对象中有一个 SecondaryId 和它们的列表很奇怪。我知道你为什么这样做,但如果你想放弃 SecondaryId 单数并保持列表,groupby 会稍微改变:
.GroupBy(
a => a.PrimaryId,
a => a.SecondaryId.First(), //take the only item
(key, innerList) => new Account { PrimaryId = key, SecondaryIds = innerList.ToList() }
)
这将能够处理次要 ID 的单个项目列表,例如这样的事情:
var accounts = new List<Account>()
{
new Account { PrimaryId = 1, SecondaryIds = new(){10} },
new Account { PrimaryId = 1, SecondaryIds = new(){12} }
};
看看你的尝试:
var rs = accounts.GroupBy(g => g.PrimaryId == 1)
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
执行.GroupBy(g => g.PrimaryId == 1)
将根据 PrimaryId 是否为 1 的评估进行分组。它不会 filter 任何东西到“只是 ID 1” - 基本上任何 PrimaryId 1 都会进入一个内部列表,而其他一切都会进入另一个内部列表。内部列表的 Key
将是 bool
。如果您的帐户列表只包含主 ID 为 1 的帐户对象,那么一切都会好起来的。如果没有它会有点失控
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
你的带有一个参数的 GroupBy() 产生了类似 List<List<Account>>
的东西,所以这意味着 s
是内部列表。它实际上是一个 IGrouping<Account>
,就像一个 Account 对象列表,但是有一个额外的键 属性 告诉你分组中所有项目的共同点。
这意味着,因为这个 .Select
给你 s
而 s
是一个帐户列表,而你只需要 SecondaryId 在该列表中的每个帐户中,您需要 另一个 Select,这次 在 s
上拉取其中每个帐户的 SecondaryId:
.Select(s => new Accounts
{
PrimaryId = 1,
SecondaryIds = s.Select(acc => acc.SecondaryId)
});
然后您想在 that Select
上调用 ToList()
.Select(s => new Accounts
{
PrimaryId = 1,
SecondaryIds = s.Select(acc => acc.SecondaryId).ToList()
});
如果我这样做的话,我会这样做:
.GroupBy(a => a.PrimaryId)
.Select(g => new Accounts
{
PrimaryId = g.Key,
SecondaryIds = g.Select(acc => acc.SecondaryId).ToList()
});
但是第 3 个参数 GroupBy 更简洁一些..
private static void Main()
{
var accounts = new List<Account>()
{
new Account { PrimaryId = 1, SecondaryId = 10 },
new Account { PrimaryId = 1, SecondaryId = 12 }
};
}
public class Account
{
public int PrimaryId { get; set; }
public IList<int> SecondaryIds { get; set; }
public int SecondaryId { get; set; }
}
假设我有上面的代码,我想按PrimaryId分组,然后将SecondaryId合并到SecondaryIds中。
例如:
// PrimaryId = 1
// SecondaryIds = 10, 12
如何实现?
到目前为止我已经这样做了
var rs = accounts.GroupBy(g => g.PrimaryId == 1)
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
当使用 .GroupBy()
后跟 .Select()
时,您需要在分组项目中向下钻取一层:
var rs = accounts
.GroupBy(a => a.PrimaryId)
.Select(gr => new Account {
PrimaryId = gr.Key,
SecondaryIds = gr.Select(account => account.SecondaryId).ToList()
});
也许我会做什么
LINQ GroupBy
以其最简单的形式采用 lambda,returns 作为分组依据的键。 .GroupBy(a => a.PrimaryId)
。它产生类似于“列表列表”的东西,其中内部列表中的所有内容都具有相同的键。
如果你做了那个分组,你可以这样探索它:
var listOfLists = accounts.GroupBy(a => a.PrimaryId);
foreach(var innerList in listOfLists){
Console.WriteLine("Key is: " + innerList.Key);
foreach(Account account in innerList)
Console.WriteLine("Sid is: " + account.SecondaryId);
}
所以它有效地将您的帐户对象放入 List<List<Account>>
之类的东西中,除了内部列表有一个 Key
属性 告诉您帐户的共同价值(PrimaryId)在内部列表中全部共享
GroupBy 有一个扩展形式,它接受第二个参数“你想将什么项目(可能来自原始对象)放入内部列表?”,我们可以这样使用它:
.GroupBy(a => a.PrimaryId, a => a.SecondaryId)
这会创建类似 List<List<int>>
的内容,因为它不是整个帐户对象,而是将 SecondaryId 拉出并将 that 放入内部列表。这很好,因为您仍然可以从 Key
中获取 PrimaryId,并且 Account
实际上还有另一种形式的 GroupBy,带有 第三个 参数,它将获取结果“列表列表”中的每个项目,并允许您使用 那个..
.GroupBy(
a => a.PrimaryId, //derive the key
a => a.SecondaryId, //derive the innerList
(key, innerList) => new Account { PrimaryId = key, SecondaryIds = innerList.ToList() }
)
您可以将第三个参数想象成您放在 groupby 末尾的 .Select()
。它略有不同,并且可能更容易使用,因为“键”和“具有该键的事物列表”之间的明确区分
所以,第一个参数 (key,
是分组键(PrimaryID
),第二个参数 innerList)
是所有 SecondaryId
的列表那个分组键。我们可以使用这两位信息来创建一个新的 Account
对象,其中设置了 Primary 和 SecondaryIds
我在评论中提到,在一个对象中有一个 SecondaryId 和它们的列表很奇怪。我知道你为什么这样做,但如果你想放弃 SecondaryId 单数并保持列表,groupby 会稍微改变:
.GroupBy(
a => a.PrimaryId,
a => a.SecondaryId.First(), //take the only item
(key, innerList) => new Account { PrimaryId = key, SecondaryIds = innerList.ToList() }
)
这将能够处理次要 ID 的单个项目列表,例如这样的事情:
var accounts = new List<Account>()
{
new Account { PrimaryId = 1, SecondaryIds = new(){10} },
new Account { PrimaryId = 1, SecondaryIds = new(){12} }
};
看看你的尝试:
var rs = accounts.GroupBy(g => g.PrimaryId == 1)
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
执行.GroupBy(g => g.PrimaryId == 1)
将根据 PrimaryId 是否为 1 的评估进行分组。它不会 filter 任何东西到“只是 ID 1” - 基本上任何 PrimaryId 1 都会进入一个内部列表,而其他一切都会进入另一个内部列表。内部列表的 Key
将是 bool
。如果您的帐户列表只包含主 ID 为 1 的帐户对象,那么一切都会好起来的。如果没有它会有点失控
.Select(s => new Accounts
{ PrimaryId = 1, SecondaryIds = new List<int>() { /*???*/ } });
你的带有一个参数的 GroupBy() 产生了类似 List<List<Account>>
的东西,所以这意味着 s
是内部列表。它实际上是一个 IGrouping<Account>
,就像一个 Account 对象列表,但是有一个额外的键 属性 告诉你分组中所有项目的共同点。
这意味着,因为这个 .Select
给你 s
而 s
是一个帐户列表,而你只需要 SecondaryId 在该列表中的每个帐户中,您需要 另一个 Select,这次 在 s
上拉取其中每个帐户的 SecondaryId:
.Select(s => new Accounts
{
PrimaryId = 1,
SecondaryIds = s.Select(acc => acc.SecondaryId)
});
然后您想在 that Select
上调用 ToList().Select(s => new Accounts
{
PrimaryId = 1,
SecondaryIds = s.Select(acc => acc.SecondaryId).ToList()
});
如果我这样做的话,我会这样做:
.GroupBy(a => a.PrimaryId)
.Select(g => new Accounts
{
PrimaryId = g.Key,
SecondaryIds = g.Select(acc => acc.SecondaryId).ToList()
});
但是第 3 个参数 GroupBy 更简洁一些..