Plz help with System.Linq.Aggregate
I have the following class
public class MainClass
{
public ClassA someProp { get; set; }
public ClassA someProp2 { get; set; }
}
public class ClassA
{
public virtual Type Types => Type.None;
}
public class ClassB:ClassA
{
public override Type Types => Type.Default;
public string FieldName { get; set; }
}
public class ClassC:ClassA
{
public override Type Types => Type.Value;
public int FieldValue { get; set; }
}
And i want to get FieldName if it is filled in ClassB or the value from ClassC if it is also filled for someProp2 and someProp
I understand how to get 1 value
//for get name
IEnumerable<string> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassB>()
.Select(classB => classB.FieldName)
//for get value
IEnumerable<int> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassC>()
.Select(classC => classC.FieldValue)
but I don’t understand how to get 2 values in 1 requests, because there will be type 2 classes ClassB and ClassC
Probably the best way would be through the Aggregate method!
Tell me how to make the conditions for the selection and the Aggregate method itself
CodePudding user response:
If you want to use .Aggregate()
to achieve your goal, I would suggest that you use an overload that lets you define a seed in addition to the accumulator function after flattening the property pairs.
It could be implemented as follows:
IEnumerable<string> values = entities
.SelectMany(ent => new[] { ent.someProp, ent.someProp2 })
.Aggregate(new List<string>(),
( fields, property ) =>
{
if (property is ClassB)
{
fields.Add(((ClassB)property).FieldName);
}
else if (property is ClassC)
{
fields.Add(((ClassC)property).FieldValue.ToString());
}
return fields;
});
Here, our seed is
new List<string>()
and our accumulator function is
( fields, property ) =>
{
// accumulation logic
return fields;
}
, containing the accumulator value parameter fields
and element parameter property
.
Our seed is the initial value of our accumulator value. For the first property
in the flattened property collection (provided by the .SelectMany()
), our accumulator value (fields
) is therefore an empty list of strings.
For each element (property
), field values are extraced based on the property's class type:
- if
property
is aClassB
,FieldName
is extracted and added to our accumulator value. - if
property
is aClassC
,FieldValue
is extracted, and its string value is added to our accumulator value. - (if
property
is neither of those classes, nothing is added tofields
).
After conditionally adding a field value to fields
, fields
is returned from the accumulator function to be used as the accumulator value in the next iteration of the accumulator function (i.e. for the next property
in the flattened collection).
For entities
given as follows:
List<MainClass> entities = new()
{
new()
{
someProp = new ClassC() { FieldValue = 4 },
someProp2 = new ClassB() { FieldName = "Flower" }
},
new()
{
someProp = new ClassA() { },
someProp2 = new ClassC() { FieldValue = 7 }
}
};
, the resulting values
will contain:
4
Flower
7
Example fiddle here.