public class Factory
{
public string Number { get; set; }
public List<OrganizationUnit> Units;
}
public class OrganizationUnit
{
public string Name { get; set; }
public string Id { get; set; }
public bool ?IsActive { get; set; }
}
public static class LocalisationRepo
{
public static List<Factory> GetFactories()
{
List<Factory> fa = new List<Factory>();
Factory factory = new Factory() { Number="F10", Units = new List<OrganizationUnit> {
new OrganizationUnit()
{ Id = "001", Name = "Quality", IsActive = false},
new OrganizationUnit() { Id = "002", Name = "Line 3", IsActive=null } ,
new OrganizationUnit { Id="003", Name="IT", IsActive=true } } };
Factory factory2 = new Factory()
{
Number = "F11",
Units = new List<OrganizationUnit> {
new OrganizationUnit()
{ Id = "001", Name = "Quality", IsActive = true},
new OrganizationUnit() { Id = "002", Name = "Line 3", IsActive=true } ,
new OrganizationUnit { Id="003", Name="IT", IsActive=true } }
};
fa.Add(factory);
fa.Add(factory2);
return fa;
}
}
有一個 linq 查詢
var factories = LocalisationRepo.GetFactories();
var fa = factories.SelectMany(f => f.Units.Where(u => (u.IsActive ?? false) == false), (f, u) => f).Distinct();
foreach (var item in fa)
{
Console.WriteLine(item.Number);
}
第一部分f => f.Units.Where(u => (u.IsActive ?? false) == false)
為我們提供IEnumerable<OrganizationUnit>
了第二個引數(f, u) => f(應用于中間序列每個元素的變換函式)“將序列的每個元素投影到IEnumerable<Out T>”
我的問題是,當在第一個引數的“輸出”時,此選擇器/委托如何從IEnumerable<OrganizationUnit>toIEnumerable<Factory>進行轉換,IEnumerable<OrganizationUnit>
應該如何考慮/理解它?
我們知道 f 是一個工廠,但“中間結果”是 OrganizationUnit 所以......如何?關于函式式編程的一些理論?
我想要沒有活動的 OrganisationUnits IsActive=false 的工廠;但我不是問如何求出結果,因為我的例子作業正常。我想知道它是如何作業的,為什么...
與 Microsoft 示例相同https://docs.microsoft.com/en-US/dotnet/api/system.linq.enumerable.selectmany?view=net-6.0
我們可以使查詢 var query = petOwners .SelectMany(po => po.Pets.Where(p => p.StartsWith("S")), (po, p) => po);
uj5u.com熱心網友回復:
我認為這里的混淆與(不必要的)使用SelectMany來過濾工廠有關。
在此宣告中:
var fa = factories.SelectMany(f => f.Units.Where(u => (u.IsActive ?? false) == false), (f, u) => f);
該(f, u) => f引數基本上忽略了 selectmany 選擇的內容(即u),并且f無論如何都選擇了原廠。
所以上述情況并非完全如此。事實證明,您的實際查詢相當于
var fa = factories.Where(f => f.Units.Any(u => (u.IsActive ?? false) == false);
這是因為,對于每個工廠,該SelectMany多載將選擇Unit您提供的每個匹配s,將其作為第三個引數的 func 引數交給您,然后等待您將其轉換為您想要的任何內容。因此它希望您將一(Factory,Unit)對轉換為some T. 您決定避免轉換,而是選擇第一個元素Factoryf。然后,SelectMany將所有此類 lambda 執行的結果組合成最終序列,在您的情況下,僅包含工廠。如果您串列中的任何工廠包含多個禁用單元,它將在最終結果中多次出現,這就是您必須Distinct為查詢“有意義”添加一個的原因。
如果你想過濾“至少有一個非活動單元的工廠”,你可以這樣做:
var fa = factories.Where(f => f.Units.Any(u => (u.IsActive ?? false) == false);
如果你想要“所有單位都處于非活動狀態的工廠”,你可以這樣做:
var fa = factories.Where(f => f.Units.All(u => (u.IsActive ?? false) == false);
我們知道 f 是一個工廠,但“中間結果”是 OrganizationUnit 所以......如何?關于函式式編程的一些理論?
“中間結果”不是 OrganizationUnit:通過這個多載,中間結果實際上是一(Factory,OrganizationResult)對,你只是Factory通過選擇f作為結果來“轉換”到它。沒有什么特別之處。
SelectMany 應該用于展平結果,您似乎不需要。
uj5u.com熱心網友回復:
SelectMany 運算子用于從稱為嵌套集合的集合集合中選擇元素。
SelectMany 從嵌套集合中回傳單個結果 您可以使用 SelectMany 從工廠獲取每個 OrganizationUnit 行。
var fa = factories.SelectMany(f => f.Units.Where(u => !(u.IsActive??false)), (factory, unit) => unit);
你正試圖在相反的方向。
重要的是不要使用 .Distinct() 來修復重復項。重復項首先不應該存在。
解決方案:
var fa = factories.Where( factory => factory.Units.Any(unit => (unit.IsActive?? false) == false));
uj5u.com熱心網友回復:
應該如何考慮/理解?
您正在對工廠串列執行 SelectMany,并且您告訴 SM 您希望它扁平化每個工廠中的單元。第二個引數表示一個函式,它采用當前正在迭代的工廠和正在迭代的單元之一。
listOfFactories.SelectMany(aFactory => aFactory.listOfTheUnits, (theCurrentFactory, oneOfTheUnitsOfTheCurrentFactory) => ...)
或以非代表條款
listOfFactories.SelectMany(GetUnitsFromAFactory, DoSomethingWithAFactoryAndUnitAndGetAnOutput)
SelectMany 在概念上只是一對嵌套的 foreach 回圈;在這些方面,它可能是這樣的:
var output = List<SomeOutput>();
foreach(var aFactory in listOfFactories){
var theCurrentFactory = aFactory;
var someEnumerable = GetUnitsFromAFactory();
foreach(var oneOfTheUnitsOfTheCurrentFactory in someEnumerable){
output.Add(DoSomethingWithAFactoryAndUnitAndGetAnOutput(theCurrentFactory, oneOfTheUnitsOfTheCurrentFactory);
}
}
//methods
IEnumerable<OrganizationUnit> GetUnitsFromAFactory(Factory f){
return f.listOfTheUnits;
}
SomeOutput DoSomethingWithAFactoryAndUnitAndGetAnOutput(Factory f, Unit u) {
...
}
與您的代碼有關的令人困惑的事情是您只從“DoSomethingWithAFactoryAndUnitAndGetAnOutput”回傳工廠,因此如果您有 3 個工廠分別具有 10、11 和 12 個單元,您最終會得到 33 個工廠的串列,第一個重復 10 個工廠,第二次重復 11 次,第三次重復 12 次。現在我們(希望)很高興 SelectMany 是一個嵌套回圈對,讓我們添加關于單位不活動的另一位
var out = new List<Factory>
foreach(var f in factories)
foreach(var u in f.Units)
if(unit.IsActive ?? false == false)
out.Add(f);
你看,你已經多次添加了一個工廠,每一個不活動的單元添加一次
然后你區分這些來折疊它們,所以整個 SelectMany 是一種浪費的練習,你可以只要求具有不活動單元的工廠。在簡單的回圈術語中,可能如下所示:
var out = new List<Factory>
foreach(var f in factories)
foreach(var u in f.Units)
if(unit.IsActive ?? false == false){
out.Add(f);
break;
}
添加單個工廠(遇到第一個非活動單元)后添加中斷停止。在逐步添加 LINQ 術語中,這更像是在做
foreach(var f in factories)
if(f.Units.Any(unit => unit.IsActive ?? false == false))
out.Add(f);
這是
factories.Where(f => f.Units.Any(unit => unit.IsActive ?? false == false))
uj5u.com熱心網友回復:
我不認為SelectMany正在做你認為它正在做的事情。我想,你認為它的回傳與過濾,只有那些所在單位的工廠IsActive是false通過某種投射的從單元的集合回工廠。那是不正確的。
第二個選擇器SelectMany是resultSelector你傳遞的:
(f, u) => f
哪里f和u是源集合的專案,以及由前一個引數(您的Where結果)投影的集合專案。這意味著對于任何滿足Where條件 ( u) 的單元,它都會回傳包含它( f)的工廠。這就是您必須呼叫的原因Distinct,因為您實際上是為每個匹配的單元獲取工廠。如果一家工廠有兩臺符合標準的設備,那么您將把同一家工廠送回兩次。Unique從結果中洗掉那些重復的工廠,但工廠本身(意味著它們內部單元的連接)不會改變。
為了“過濾”子集合,您需要重新投影工廠:
var fa = factories.Select(f => new Factory(
{
Number = f.Number,
Units = f.Units.Where(u => (u.IsActive ?? false) == false).ToList();
}
)
);
或者,或者,回圈遍歷工廠并洗掉IsActiveis的單位true。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/390897.html
