Linq How to deflate list of class/self join - c#

I have a list of the following class
class testdata {
public string code { get; set; }
public string[] values { get; set; }
}
how to perform join such that I get list of code and values, i.e values will repeat with every code, e.g if there are two items in list like
1."code1",{"value1","value2"}
2."code2",{"value1","value2","value3"}
I want the final to be in format like
Code1,Value1
code1,Value2
Code2,value1
code3,value2
code3,value3
I want to achieve it via LINQ and anonymous class, I am able to do it with loop and by creating a class as per final structure.

myList.SelectMany(td => td.values.Select(v => new { td.code, v } ))
SelectMany says "take many IEnumerables and concatenate their elements into a single IEnumerable"
td is a single element in your list. It is a single instance of testdata
So now you can do a Select over td.values to create the individual anonymous objects. Select will return an IEnumerable, but wrapped inside a SelectMany, many instances ofIEnumerable<anonymous object> will be flattened into a single IEnumerable<anonymous object>

Try this
List<TestData> testData = new List<TestData>() {
new TestData() { code = "Code1", values = new string[] {"Value1","Value2"}},
new TestData() { code = "Code2", values = new string[] {"value1"}},
new TestData() { code = "code3", values = new string[] {"value2","value3"}}
};
var results = testData.Select(x => x.values.Select(y => new { code = x.code, value = y })).SelectMany(y => y).ToList();

Related

How to add items to a list from List<IEnumerable> using C# Linq

I have a List T , and I have a List of IEnumerable query.
I want to add the selection/ all the values of List of IEnumerable to this List T.
I tried below but it shows error : Cannot implicitly convert type 'List IEnumerable ' to a generic list Reason.
I am very new to LINQ, please guide. I tried the following :
public class Reason
{
public int ReasonId { get; set; }
public int OrderId { get; set; }
}
var newReason = new List<Reason>();
newReason = reasons?.Select(res => res.Select(re => new Reason()
{
ReasonId = re.ReasonId,
OrderId = re.OrderId,
})).ToList();
You are trying to create list of Reason instance by flattening nested reasons list, try SelectMany()
newReason = reasons?.SelectMany(re => new Reason()
{
ReasonId = re.ReasonId,
OrderId = re.OrderId,
})?.ToList()
?? new List<Reason>();
Select() inside Select() will return IEnumerable<IEnumerable<Reason>>, and you need flatten list of Reason class i.e. List<Reason> so use SelectMany()

how to use string.join to join all values from an object array?

I have the same situation as this user how to use string.join to join value from an object array? in this question. However, I want to join all values from the object instead of only 1 value.
To recap my question:
I have an array of object e.g:
MyObject[] objs;
and within MyObject it contains 3 string property,
object[0].stringValue1
object[0].stringValue2
object[0].stringValue3
If I want to join the whole array of objects by all of their stringValues (stringValues1,2 and 3), how can I do it?
I think selector doesn’t allow me to select several elements, then how to use string.join to join several values from an object array?
See below for example usage of the two extension methods provided in the implementation section below. The first allows you to select the properties to include, and the second includes all string properties of the object in the source collection which I believe is the exact answer to your question.
Example Usage
Note, the resulting output from the two examples below are ordered differently as a result of how each implementation works, however the results are otherwise identical as a result of the first example specifying all string properties on the MyObj sample type
Live Fiddle Example
// Test Object
public class MyObj
{
public MyObj(string prop1, string prop2, string prop3)
{
Prop1 = prop1;
Prop2 = prop2;
Prop3 = prop3;
}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Sample Data
var list = new List<MyObj> {
new MyObj("A1", "B1", "C1"),
new MyObj("A1", "B2", "C2"),
new MyObj("A3", "B3", "C3")
};
Samples using above object and data
// Example #1 - All properties separated by single separator
Console.WriteLine(list.Join(colSeparator: ','));
// RESULT: A1,A1,A3,B1,B2,B3,C1,C2,C3
// Example #2 - Object property separator, and different object separator
Console.WriteLine(list.Join(colSeparator: ',', rowSeparator: '\n'));
// RESULT: A1,B1,C1
A1,B2,C2
A3,B3,C3
Implementation
public static class EnumerableStringJoinExtension
{
public static string Join<T>(this IEnumerable<T> values, char colSeparator, char? rowSeparator = null)
{
var strProperties = typeof(T).GetProperties().Where(r=>r.PropertyType == typeof(string));
var sb = new StringBuilder();
foreach(var val in values)
sb.Append(string.Join(colSeparator, strProperties.Select(r=> r.GetValue(val)))).Append(rowSeparator ?? colSeparator);
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
}
A possible way to solve it is to first create an array of each object's properties (using .Select()), and then flatten the resulting property arrays by using .SelectMany(). Both of those methods are found in the System.Linq namespace. The resulting IEnumerable<string> object's items can then be joined using string.Join().
If MyObject is defined as
class MyObject
{
public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }
}
and you define objects as
List<MyObject> objects = new()
{
new() { First = "ABC", Second = "DEF", Third = "GHI" },
new() { First = "JKL", Second = "MNO", Third = "PQR" },
new() { First = "STU", Second = "VWX", Third = "YZ" },
};
, a possible implementation is:
var flattenedObjects = objects
.Select(obj => new[] { obj.First, obj.Second, obj.Third })
.SelectMany(properties => properties);
var objectString = string.Join("_", flattenedObjects);
Printing the value of objectString to the console gives:
ABC_DEF_GHI_JKL_MNO_PQR_STU_VWX_YZ
Example fiddle here.
If you just want a one-line, handy C# without hard-coding property name you could try this:
string.Join("\n", objs.Select(o => string.Join("_", o.GetType().GetProperties().Select(p => p.GetValue(o)))));
This produces the following result:
"A_B_C\nD_E_F\nG_H_I"
with the object array as:
var objs = new MyObject[]
{
new MyObject("A", "B", "C"),
new MyObject("D", "E", "F"),
new MyObject("G", "H", "I")
};
But please note that if your class has properties in other types then you might need a Where() between GetProperties() and the second Select() to exclude unwanted properties.

How to find Distinc from the multiple lists using LINQ?

I need to find all the distinct items (religions) from the entire collection of countries, where everyone of them has its own list of the items (religions). Here is my object class:
public class Country
{
public string Name { get; }
public List<string> Religions { get; }
public Country(string name, List<string> religions)
{
Name = name;
Religions = religions;
}
public static List<Country> GetCountries()
{
return new List<Country>()
{
new Country( "Venezuela", new List<string> { "Roman Catholic", "Protestant" } ),
new Country( "Peru", new List<string> { "Roman Catholic", "Evangelical" } ),
new Country( "Paraguay", new List<string> { "Roman Catholic", "Protestant" } ),
new Country( "Bolivia", new List<string> { "Roman Catholic", "Evangelical", "Protestant" } )
};
}
public override string ToString() =>
$"\n{Name} \nReligions: {string.Join(", ", Religions)}";
}
Here is my Main class:
List<Country> countries = Country.GetCountries();
AllReligions(countries);
Console.ReadKey();
static void AllReligions(List<Country> countries)
{
var distinctReligions = countries
.Select(r => new { r.Religions })
.Distinct()
.ToList();
Console.WriteLine("Religions in South America:");
foreach (var rel in distinctReligions)
Console.WriteLine(rel);
}
I am on my 5th iteration of the code and one of the problems is that I don't know where the error is happening - inside my DISTINCT function or inside my printout function. Any help would be greatly appreciated. This is the printout:
Both are wrong. Both work on the anonymous objects, not the religion strings.
To get the distinct religions you need to use SelectMany to "flatten" the country/religion graph:
IEnumerable<string> religions=Country
.GetCountries()
.SelectMany(country=>country.Religions)
.Distinct();
Console.WriteLine("Religions in South America:");
foreach (var rel in religions)
{
Console.WriteLine(rel);
}
The equivalent in query form would be :
var religions = ( from country in Country.GetCountries()
from religion in country.Religions
select religion
).Distinct();
You have several problems here.
What you think you get with .Select(r => new { r.Religions }) - it gives you list of lists. What you want is SelectMany(r => r.Religions) - that way, you will get list of religions, no list of anonymous objects with list in its property.
Distinct uses basic comparison, you need to write custom comparer and provide it to overload:
Distinct<TSource>(IEnumerable<TSource>, IEqualityComparer<TSource>)
You need to implement IEqualityComparer<Religion>.
UPDATE: religion is just a string, so you do not need to implement one, after using SelectMany everything should be just fine.
Your "printout" function - you use Console.WriteLine which invokes just ToString on objects - as you have lists there, it looks weird :) After suggestion of using SelectMany it should go away.

Linq to iterate through a collection and add another collection to its member

I have a situation where I need to iterate through a collection and add another collection to one of its member using Linq.
For example I have this class
public class Product
{
public string Car { get; set; }
public IEnumerable<Part> Part { get; set; }
}
This class would be within a collection like
IEnumerable<Product> ProductList
How can I populate the Part-property for each Product using GetPartData() with Linq
private IEnumerable<IEnumerable<Part>> GetPartData()
{
return new List<List<Part>>() {
new List<Part>{
new Part(){PartType="11",PartValue=1},
new Part(){PartType="12",PartValue=2}
},
new List<Part>{
new Part(){PartType="21",PartValue=1},
new Part(){PartType="22",PartValue=2}
}
};
}
So ultimately, my ProductList[0].Part should be equal to GetPartData()[0]
If both sequences should be linked via index you can use Enumerable.Zip:
ProductList = ProductList.Zip(GetPartData()
, (product, part) => new Product
{
Car = product.Car,
Part = part
})
.ToList();
Basically, you need to enumerate two IEnumerable at a time to match items from both. The ProductList and the result of GetPartData.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
foreach((product, part) in (products, parts)) // will not work :(
{
product.Part = part;
}
Solutions has been debated before.
The Zip method will do it.
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, (product, part) => product.Part = part).ToList();
The ToList() is really important, to force the execution.
If you are not comfortable with the lambda, you can do it like this:
// The two IEnumerable
var products = ProductList;
var parts = GetPartData();
products.Zip(parts, ProductPartAssociation).ToList();
...
Product ProductPartAssociation(Product product, IEnumerable<Part> part)
{
product.Part = part;
return product; // Actually not used.
}
The result of the Zip is an IEnumerable of whatever the ProductPartAssociation function return. You don't care about it, because what you need is just to be sure that the ProductPartAssociation is executed.

LINQ select one field from list of DTO objects to array

I have DTO class that defines order line like this:
public class Line
{
public string Sku { get; set; }
public int Qty { get; set; }
}
A list of type Line is populated like so:
List<Line> myLines = new List<Line>();
myLines.Add(new Line() { Sku = "ABCD1", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD2", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD3", Qty = 1 });
What I want is to use LINQ to get an array of SKUs from the myLines List. How can I go about doing that?
I am currently doing it manually like this ...
// Get SKU List
List<string> mySKUs = new List<string>();
foreach (Line myLine in myLines)
mySKUs.Add(myLine.Sku);
string[] mySKUsArray = mySKUs.ToArray();
I was trying to google for a solution, but I wasn't sure how to word the question...
P.S. is there any benefit/performance gain in using LINQ method to achieve what I am currently doing with foreach?
You can use:
var mySKUs = myLines.Select(l => l.Sku).ToList();
The Select method, in this case, performs a mapping from IEnumerable<Line> to IEnumerable<string> (the SKU), then ToList() converts it to a List<string>.
Note that this requires using System.Linq; to be at the top of your .cs file.
This is very simple in LinQ... You can use the select statement to get an Enumerable of properties of the objects.
var mySkus = myLines.Select(x => x.Sku);
Or if you want it as an Array just do...
var mySkus = myLines.Select(x => x.Sku).ToArray();
I think you're looking for;
string[] skus = myLines.Select(x => x.Sku).ToArray();
However, if you're going to iterate over the sku's in subsequent code I recommend not using the ToArray() bit as it forces the queries execution prematurely and makes the applications performance worse. Instead you can just do;
var skus = myLines.Select(x => x.Sku); // produce IEnumerable<string>
foreach (string sku in skus) // forces execution of the query
You can select all Sku elements of your myLines list and then convert the result to an array.
string[] mySKUsArray = myLines.Select(x=>x.Sku).ToArray();
In the case you're interested in extremely minor, almost immeasurable performance increases, add a constructor to your Line class, giving you such:
public class Line
{
public Line(string sku, int qty)
{
this.Sku = sku;
this.Qty = qty;
}
public string Sku { get; set; }
public int Qty { get; set; }
}
Then create a specialized collection class based on List<Line> with one new method, Add:
public class LineList : List<Line>
{
public void Add(string sku, int qty)
{
this.Add(new Line(sku, qty));
}
}
Then the code which populates your list gets a bit less verbose by using a collection initializer:
LineList myLines = new LineList
{
{ "ABCD1", 1 },
{ "ABCD2", 1 },
{ "ABCD3", 1 }
};
And, of course, as the other answers state, it's trivial to extract the SKUs into a string array with LINQ:
string[] mySKUsArray = myLines.Select(myLine => myLine.Sku).ToArray();

Categories