Reputation: 58
I've got two classes like this:
class SomeObject_source
{
public string Model;
public string Color;
public string Size;
}
class SomeObject_target
{
public string Model;
public string Color;
public string Size;
public double Quantity;
public double Nett;
}
Then we are getting an instance of the SomeObject_source with the following data structure:
SomeObject_source someObject_source = new SomeObject_source
{
Model = "Model_1|Model_2|Model_3|Model_4|Model_5",
Color = "Black|Black|White|Blue|Red",
Size = "S^1^0.5|M^2^1.0|L^1^0.6|S^3^1.5|S^1^0.6"
};
The Size string has the following pattern: size^quantity^nett. But sometimes there is an object with a simple data like:
SomeObject_source someObject_source = new SomeObject_source
{
Model = "Model_1",
Color = "Black",
Size = "S"
};
I'm trying to figure out how do an elegant single LINQ query to split the following data into a List of SomeObject_target, so that the result would be equal to:
List<SomeObject_target> someObject_Target_List = new List<SomeObject_target>
{
new SomeObject_target
{
Model = "Model_1",
Color = "Black",
Size = "S",
Quantity = 1,
Nett = 0.5
},
new SomeObject_target
{
Model = "Model_2",
Color = "Black",
Size = "M",
Quantity = 2,
Nett = 1
},
new SomeObject_target
{
Model = "Model_3",
Color = "White",
Size = "L",
Quantity = 1,
Nett = 0.6
},
new SomeObject_target
{
Model = "Model_4",
Color = "Blue",
Size = "S",
Quantity = 3,
Nett = 1.5
},
new SomeObject_target
{
Model = "Model_5",
Color = "Red",
Size = "S",
Quantity = 1,
Nett = 0.6
},
};
For now i'm doing as follow:
char Delimeter_main = '|';
char Delimeter_inner = '^';
List<SomeObject_target> someObject_Target_List_ = new List<SomeObject_target>();
for (int ind = 0; ind < someObject_source.Model.Split(Delimeter_main).Count(); ind++)
{
string Model = someObject_source.Model.Split(Delimeter_main)[ind];
string Color = someObject_source.Color.Split(Delimeter_main)[ind];
string Size_unparsed = someObject_source.Size.Split(Delimeter_main)[ind];
if (Size_unparsed.Contains(Delimeter_inner))
{
string size = Size_unparsed.Split(Delimeter_inner)[0];
double quantity = double.TryParse(Size_unparsed.Split(Delimeter_inner)[1], out double _quantity) ? _quantity : 1;
double nett = double.TryParse(Size_unparsed.Split(Delimeter_inner)[2], out double _nett) ? _nett : 1;
someObject_Target_List_.Add(new SomeObject_target
{
Model = Model,
Color = Color,
Size = size,
Quantity = quantity,
Nett = nett
});
}
else
{
someObject_Target_List_.Add(new SomeObject_target
{
Model = Model,
Color = Color,
Size = Size_unparsed,
Quantity = 1,
Nett = 1
});
}
}
But this obviously looks weird as well as the data architecture overall. Is there any LINQ query to accomplish that in single elegant query?
Upvotes: 0
Views: 242
Reputation: 2734
var result = someObject_source.Model.Split(Delimeter_main)
.Zip(someObject_source.Color.Split(Delimeter_main), (x, y) => new { x, y })
.Zip(someObject_source.Size.Split(Delimeter_main), (zip1, z) =>
{
var secondSplit = z.Split(Delimeter_inner);
return new SomeObject_target
{
Model = zip1.x,
Color = zip1.y,
Size = secondSplit[0],
Quantity = double.TryParse(secondSplit[1], out double _quantity) ? _quantity : 1,
Nett = double.TryParse(secondSplit[2], out double _nett) ? _nett : 1,
};
});
A little more readable, without the annonymous object wraper for the first Zip result.
var results = someObject_source.Model.Split(Delimeter_main)
.ZipThree(
someObject_source.Color.Split(Delimeter_main),
someObject_source.Size.Split(Delimeter_main),
(x, y, z) => {
var secondSplit = z.Split(Delimeter_inner);
return new SomeObject_target
{
Model = x,
Color = y,
Size = secondSplit[0],
Quantity = double.TryParse(secondSplit[1], out double _quantity) ? _quantity : 1,
Nett = double.TryParse(secondSplit[2], out double _nett) ? _nett : 1,
};
}
);
Upvotes: 1
Reputation: 26917
Using this Pivot
extension method that pivots IEnumerable<IEnumerable<T>>
(note: this isn't particularly efficient, but a better/faster method is quite a bit longer):
public static class IEnumerableExt {
// Pivot IEnumerable<IEnumerable<T>> by grouping matching positions of each sub-IEnumerable<T>
// itemGroups - source data
public static IEnumerable<IEnumerable<T>> Pivot<T>(this IEnumerable<IEnumerable<T>> itemGroups) =>
itemGroups.Select(g => g.Select((item, i) => (item, i)))
.SelectMany(g => g)
.GroupBy(ii => ii.i, si => si.item);
}
You can process someObject_source
as follows:
char Delimeter_main = '|';
char Delimeter_inner = '^';
var someObject_Target_List =
new[] {
someObject_source.Model.Split(Delimeter_main),
someObject_source.Color.Split(Delimeter_main),
someObject_source.Size.Split(Delimeter_main)
} // Create IEnumerable<string>[] (== IEnumerable<IEnumerable<string>>)
.Pivot()
.Select(t => t.ToList()) // project to IEnumerable<List<string>> to access members
.Select(t => (model: t[0], color: t[1], rest: t[2].Split(Delimeter_inner))) // project to IEnumerable<ValueTuple> to access members
.Select(t => new SomeObject_target {
Model = t.model,
Color = t.color,
Size = t.rest[0],
Quantity = t.rest.Length > 1 ? double.Parse(t.rest[1]) : default(double),
Nett = t.rest.Length > 2 ? double.Parse(t.rest[2]) : default(double),
})
.ToList();
Upvotes: 1