I want to make one collection of three according to certain conditions (with LINQ). let's say I have a Class:
class Collection
{
public int id;
public string name;
public double weight;
}
Then I'm creating collections:
List<Collection> collection1 = new()
{
new Collection() { id = 11, name = "Abraham 1", weight = 1.1 },
new Collection() { id = 12, name = "Abraham 2", weight = 1.2 },
new Collection() { id = 13, name = "Abraham 3", weight = 1.3 },
};
List<Collection> collection2 = new()
{
new Collection() { id = 21, name = "Bill 1", weight = 2.1 },
new Collection() { id = 22, name = "Bill 2", weight = 2.2 },
new Collection() { id = 23, name = "Bill 3", weight = 2.3 },
};
List<Collection> collection3 = new()
{
new Collection() { id = 31, name = "David 1", weight = 3.1 },
new Collection() { id = 32, name = "David 2", weight = 3.2 },
new Collection() { id = 33, name = "David 3", weight = 3.3 },
};
TODO 1. Condition: get 1st column from 1st collection, 2nd column from 2nd collection, 3rd column from 3rd column. result should be:
{
new Collection() { id = 11, name = "Bill 1", weight = 3.1 },
new Collection() { id = 12, name = "Bill 2", weight = 3.2 },
new Collection() { id = 13, name = "Bill 3", weight = 3.1 }
}
TODO 2. Second case condition: get first elements from columns of all collections. result should be:
{
new Collection() { id = 11, name = "Abraham 1", weight = 1.1 },
new Collection() { id = 21, name = "Bill 1", weight = 2.1 },
new Collection() { id = 31, name = "David 1", weight = 3.1 }
}
Please help.
Using C# 10 and .Net 6, just use Enumerable.Zip:
var todo1 = collection1.Zip(collection2, collection3)
.Select(t => new Collection { id = t.First.id, name = t.Second.name, weight = t.Third.weight })
.ToList();
var todo2 = collection1.Zip(collection2, collection3)
.Select(t => new List<Collection> { t.First, t.Second, t.Third })
.First();
If you don't have the three argument Zip that returns a tuple, just roll your own, e.g. for Todo1:
var td1 = collection1.Zip(collection2, (c1, c2) => (c1, c2))
.Zip(collection3, (t12, c3) => (First: t12.c1, Second: t12.c2, Third: c3))
.Select(t => new Collection { id = t.First.id, name = t.Second.name, weight = t.Third.weight })
.ToList();
If you want to merge multiple collections into one collection using LINQ then do this:
List<Collection> result = collection1.Concat(collection2).OrderBy(x => x.id).ToList();
Related
I am trying to visualize data from database in charts using LiveCharts library. I have managed to get some of them working fine. However I am having hard times with PieCharts. I want to simply display data in two slices. For that matter I have column in DB with name AppIsRunning.
I have made a simple example that is working fine and displaying data in slices as expected:
List<DataModel> records = new List<DataModel>();
records.Add(new DataModel { Id = 1, Revenue = 43, Name = "Item 1", AppIsRunning = true });
records.Add(new DataModel { Id = 2, Revenue = 23, Name = "Item 2", AppIsRunning = true });
records.Add(new DataModel { Id = 3, Revenue = 13, Name = "Item 3", AppIsRunning = true });
records.Add(new DataModel { Id = 4, Revenue = 87, Name = "Item 4", AppIsRunning = true });
records.Add(new DataModel { Id = 5, Revenue = 23, Name = "Item 5", AppIsRunning = true });
IEnumerable<ISeries> result1 = records.Select(x =>
new PieSeries<double>
{
Values = new List<double> { x.Revenue },
Name = x.Name,
});
Now I would like to group data to display only 2 slices AppIsRunning true/false and for Values have Revenue. So I have ended up with this code:
List<DataModel> records = new List<DataModel>();
records.Add(new DataModel { Id = 1, Revenue = 43, Name = "Item 1", AppIsRunning = true });
records.Add(new DataModel { Id = 2, Revenue = 23, Name = "Item 2", AppIsRunning = true });
records.Add(new DataModel { Id = 3, Revenue = 13, Name = "Item 3", AppIsRunning = false });
records.Add(new DataModel { Id = 4, Revenue = 87, Name = "Item 4", AppIsRunning = true });
records.Add(new DataModel { Id = 5, Revenue = 23, Name = "Item 5", AppIsRunning = true });
IEnumerable<ISeries> result1 = records
.GroupBy(g => g.AppIsRunning)
.Select(item => new PieSeries<double>
{
Values = item.Select(x => Convert.ToDouble(x.Revenue)),
Name = item.Key ? "Running" : "Not running",
});
However this makes AppIsRunning true section divided into 4 sub-slices = 4 AppIsRunning TRUE values. Please see screenshot with 4 sub-sections in blue slice:
My question is how to get rid of these 4 sub-sections and group that data into one? I need only 2 slices, no need to divide slices into sub-sections.
Here is original example from LiveCharts:
this.ActivityChartSeries = new ISeries[]
{
new PieSeries<double> { Values = new double[] { 2 }, Name = "Section 1"},
new PieSeries<double> { Values = new double[] { 21 }, Name = "Section 2"},
new PieSeries<double> { Values = new double[] { 28 }, Name = "Section 3"},
new PieSeries<double> { Values = new double[] { 2 }, Name = "Section 4"},
new PieSeries<double> { Values = new double[] { 52 }, Name = "Section 5"},
};
https://github.com/beto-rodriguez/LiveCharts2/blob/master/samples/ViewModelsSamples/Pies/Basic/ViewModel.cs
Judging from the example, you want to see a breakdown of total (sum of) revenue as a single value for each status. To do that, you could do:
IEnumerable<ISeries> result1 = records
.GroupBy(g => g.AppIsRunning)
.Select(item => new PieSeries<double>
{
Values = new List<double> {item.Sum(x => Convert.ToDouble(x.Revenue))},
Name = item.Key ? "Running" : "Not running",
});
I have the below set of data
Where each City belongs to a specific Department, which belongs to a specific Region, which belongs to a specific Country (in this case there is only one country: France).
This data is contained in a CSV file which I can read from on a row-by-row basis, however my goal is to convert this data into a tree structure (with France being at the root).
Each of these nodes will be given a specific Id value, which is something I've already gone and done, but the tricky part is that each node here must also contain a ParentId (for instance Belley and Gex need the ParentId of Ain, but Moulins and Vichy need the ParentId of Aller).
Below is a snippet of code I've written that has assigned an Id value to each name in this data set, along with some other values:
int id = 0;
List<CoverageAreaLevel> coverageAreas = GetCoverageAreaDataFromCsv(path, true);
List<LevelList> levelLists = new List<LevelList>
{
new LevelList { Names = coverageAreas.Select(a => a.Level1).Distinct().ToList(), Level = "1" },
new LevelList { Names = coverageAreas.Select(a => a.Level2).Distinct().ToList(), Level = "2" },
new LevelList { Names = coverageAreas.Select(a => a.Level3).Distinct().ToList(), Level = "3" },
new LevelList { Names = coverageAreas.Select(a => a.Level4).Distinct().ToList(), Level = "4" }
};
List<CoverageArea> newCoverageAreas = new List<CoverageArea>();
foreach (LevelList levelList in levelLists)
{
foreach (string name in levelList.Names)
{
CoverageArea coverageArea = new CoverageArea
{
Id = id++.ToString(),
Description = name,
FullDescription = name,
Level = levelList.Level
};
newCoverageAreas.Add(coverageArea);
}
}
The levelLists variable contains a sort-of heirarchical structure of the data that I'm looking for, but none of the items in that list are linked together by anything.
Any idea of how this could be implemented? I can manually figure out each ParentId, but I'd like to automate this process, especially if this needs to be done in the future.
The solution from #Camilo is really good and pragmatic. I would also suggest the use of a tree.
A sample implementation:
var countries = models.GroupBy(xco => xco.Country)
.Select((xco, index) =>
{
var country = new Tree<String>();
country.Value = xco.Key;
country.Children = xco.GroupBy(xr => xr.Region)
.Select((xr, xrIndex) =>
{
var region = new Tree<String>();
region.Value = xr.Key;
region.Parent = country;
region.Children =
xr.GroupBy(xd => xd.Department)
.Select((xd, index) =>
{
var department = new Tree<String>();
department.Value = xd.Key;
department.Parent = region;
department.Children = xd
.Select(xc => new Tree<String> { Value = xc.City, Parent = department });
return department;
});
return region;
});
return country;
});
public class Tree<T>
{
public IEnumerable<Tree<T>> Children;
public T Value;
public Tree<T> Parent;
}
One way you could solve this is by building dictionaries with the names and IDs of each level.
Assuming you have data like this:
var models = new List<Model>
{
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceA" },
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceB" },
new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept2", City = "FranceC" },
new Model { Country = "France", Region = "FranceRegionB", Department = "FranceDept3", City = "FranceD" },
new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept1", City = "ItalyA" },
new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept2", City = "ItalyB" },
};
You could do something like this, which can probably be improved further if needed:
var countries = models.GroupBy(x => x.Country)
.Select((x, index) => Tuple.Create(x.Key, new { Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var regions = models.GroupBy(x => x.Region)
.Select((x, index) => Tuple.Create(x.Key, new { ParentId = countries[x.First().Country].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var departments = models.GroupBy(x => x.Department)
.Select((x, index) => Tuple.Create(x.Key, new { ParentId = regions[x.First().Region].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
var cities = models
.Select((x, index) => Tuple.Create(x.City, new { ParentId = departments[x.Department].Id, Id = index + 1 }))
.ToDictionary(x => x.Item1, x => x.Item2);
The main idea is to leverage the index parameter of the Select method and the speed of dictionaries to find the parent ID.
Sample output from a fiddle:
countries:
[France, { Id = 1 }],
[Italy, { Id = 2 }]
regions:
[FranceRegionA, { ParentId = 1, Id = 1 }],
[FranceRegionB, { ParentId = 1, Id = 2 }],
[ItalyRegionA, { ParentId = 2, Id = 3 }]
departments:
[FranceDept1, { ParentId = 1, Id = 1 }],
[FranceDept2, { ParentId = 1, Id = 2 }],
[FranceDept3, { ParentId = 2, Id = 3 }],
[ItalyDept1, { ParentId = 3, Id = 4 }],
[ItalyDept2, { ParentId = 3, Id = 5 }]
cities:
[FranceA, { ParentId = 1, Id = 1 }],
[FranceB, { ParentId = 1, Id = 2 }],
[FranceC, { ParentId = 2, Id = 3 }],
[FranceD, { ParentId = 3, Id = 4 }],
[ItalyA, { ParentId = 4, Id = 5 }],
[ItalyB, { ParentId = 5, Id = 6 }]
Apologies if this is asked elsewhere however I have searched around for a suitable answer, however most of the information I have found allows for single child item only.
I'm trying to write a program that displays information in a treeview style interface. The problem I'm facing is that I'm reading the following class from my backend DB:
public class InputClass
{
public int id { get; set; }
public string text { get; set; }
public string icon { get; set; }
public int? parentId { get; set; }
}
and I'm trying to convert it to a list of the following type:
public class OutputClass
{
public int id { get; set; }
public string text { get; set; }
public string icon { get; set; }
public List<OutputClass> children { get; set; }
}
as you can see, the children property will populate when it finds it's parent item.
As an example - the following list:
var inputList = new List<InputClass>();
inputList.Add(new InputClass() { id = 1, text = "Item #1"});
inputList.Add(new InputClass() { id = 2, text = "Item #2" });
inputList.Add(new InputClass() { id = 3, text = "Item #3" });
inputList.Add(new InputClass() { id = 4, text = "SubItem #1", parentId = 1 });
inputList.Add(new InputClass() { id = 5, text = "SubItem #2", parentId = 1 });
inputList.Add(new InputClass() { id = 6, text = "SubItem #3", parentId = 2 });
should output as:
Item #1
----SubItem #1
----SubItem #2
Item #2
----SubItem #3
Item #3
the number of elements in the children list should not be limited to just one. Any ideas on how to sort this properly?
var mapping = inputList
// for each input element, capture the parent id and create the respective output object
.Select(input => new {
ParentId = input.parentId,
Obj = new OutputClass() { id = input.id, text = input.text, icon = input.icon, children = new List<OutputClass>() }
})
// create a dictionary so we can look up the elements by id
.ToDictionary(x => x.Obj.id);
// create target list
List<OutputClass> output = new List<OutputClass>();
// loop through all elements
foreach (var x in mapping.Values)
{
// if the element has a parent id
if (x.ParentId.HasValue)
{
// find the parent object …
OutputClass parentObj = mapping[x.ParentId.Value].Obj;
// … and add this object to the parent’s child list
parentObj.children.Add(x.Obj);
}
else
{
// otherwise this is a root element, so add it to the target list
output.Add(x.Obj);
}
}
The result will be a list that contains the input elements with the respective hiearchy.
This is a solution in linear time, and it only loops through the items twice. Furthermore, it also supports hierarchies of multiple levels, so you could have items that have a parent id 5 producing a third level, etc.
To produce the output, you could write a recursive function like this:
public static void Print(IEnumerable<OutputClass> elements, string indent="")
{
foreach (OutputClass element in elements)
{
Console.WriteLine("{0}{1} {2}", indent, element.id, element.text);
Print(element.children, indent + " ");
}
}
For your input, this produces the following result:
1 Item #1
4 SubItem #1
5 SubItem #2
2 Item #2
6 SubItem #3
3 Item #3
And just as an example, the following input list produces the output below, using the same conversion code as above:
var inputList = new List<InputClass>()
{
new InputClass() { id = 1, text = "Item 1" },
new InputClass() { id = 2, text = "Item 2" },
new InputClass() { id = 3, text = "Item 3" },
new InputClass() { id = 4, text = "SubItem 1.1", parentId = 1 },
new InputClass() { id = 5, text = "SubItem 1.2", parentId = 1 },
new InputClass() { id = 6, text = "SubItem 2.1", parentId = 2 },
new InputClass() { id = 7, text = "SubItem 2.2", parentId = 2 },
new InputClass() { id = 8, text = "SubItem 1.2.1", parentId = 5 },
new InputClass() { id = 9, text = "SubItem 1.2.2", parentId = 5 },
new InputClass() { id = 10, text = "SubItem 1.2.1.1", parentId = 8 },
new InputClass() { id = 11, text = "SubItem 2.1.1", parentId = 6 },
new InputClass() { id = 12, text = "SubItem 2.1.1.1", parentId = 11 },
new InputClass() { id = 13, text = "SubItem 2.1.1.1.1", parentId = 12 },
new InputClass() { id = 14, text = "SubItem 2.1.1.1.2", parentId = 12 }
};
Output:
1 Item 1
4 SubItem 1.1
5 SubItem 1.2
8 SubItem 1.2.1
10 SubItem 1.2.1.1
9 SubItem 1.2.2
2 Item 2
6 SubItem 2.1
11 SubItem 2.1.1
12 SubItem 2.1.1.1
13 SubItem 2.1.1.1.1
14 SubItem 2.1.1.1.2
7 SubItem 2.2
3 Item 3
The following code should print what you need:
static void Main(string[] args)
{
var inputList = new List<InputClass>();
inputList.Add(new InputClass() { id = 1, text = "Item #1" });
inputList.Add(new InputClass() { id = 2, text = "Item #2" });
inputList.Add(new InputClass() { id = 3, text = "Item #3" });
inputList.Add(new InputClass() { id = 4, text = "SubItem #1", parentId = 1 });
inputList.Add(new InputClass() { id = 5, text = "SubItem #2", parentId = 1 });
inputList.Add(new InputClass() { id = 6, text = "SubItem #3", parentId = 2 });
var outputList = inputList
.Where(i => !i.parentId.HasValue) // Just get the parents
.Select(i => new OutputClass()
{
id = i.id,
icon = i.icon,
text = i.text,
children = inputList
.Where(x => x.parentId == i.id)
.Select(x => new OutputClass()
{
id = x.id,
icon = x.icon,
text = x.text,
}).ToList()
}).ToList();
foreach (var output in outputList)
{
Console.WriteLine(output.text);
output.children.ForEach(c => Console.WriteLine($"\t {c.text}"));
}
Console.ReadLine();
}
The output is:
Item #1
SubItem #1
SubItem #2
Item #2
SubItem #3
Item #3
I have a table that holds a hierarchy as below
ParentChildMap
{
parent_id,
child_id
}
Another table holds the details of each member in the Map
Member_Details
{
Member_Id,
Member_Name
}
Sometimes the relation can be as simple as Parent--->Child or sometimes the relation can have multiple levels such as GG-GrandFather--> G-GrandFather---> GrandFather ---> Parent --->Child.
What I want to do is to list all Children of a given family with their details.
Can somebody help me with the most efficient LINQ query for this?
I realize this question has been unanswered for more than 4 years, but it seemed like a fun question. I think the following approach should work with a Linq query. I'm basically postulating that children are not parents. So if this is true, then a relationship left-join on itself should produce all nodes that are not parents. Once this is done a regular join to the details will match the name of the child node. Following is my example code:
void Main()
{
// 1
// 2 3
// 4
// 5 6
// Child nodes are all those that are not parents: i.e.: 5, 6, 3
var details = new[] {
new Member_Details { Member_Id = 1, Member_Name = "Node 1" },
new Member_Details { Member_Id = 2, Member_Name = "Node 2" },
new Member_Details { Member_Id = 3, Member_Name = "Node 3" },
new Member_Details { Member_Id = 4, Member_Name = "Node 4" },
new Member_Details { Member_Id = 5, Member_Name = "Node 5" },
new Member_Details { Member_Id = 6, Member_Name = "Node 6" },
};
var relationships = new[] {
new ParentChildMap { parent_id = 1, child_id = 2 },
new ParentChildMap { parent_id = 1, child_id = 3 },
new ParentChildMap { parent_id = 2, child_id = 4 },
new ParentChildMap { parent_id = 4, child_id = 5 },
new ParentChildMap { parent_id = 4, child_id = 6 }
};
var children = relationships
.GroupJoin(relationships, r1 => r1.child_id, r2 => r2.parent_id, (r1, r2) => r2
.Select(x => new { Inner = r1.child_id, Outer = x.child_id})
.DefaultIfEmpty(new { Inner = r1.child_id, Outer = 0 }))
.SelectMany(x => x)
.Where(x => x.Outer == 0)
.Join(details, r => r.Inner, d => d.Member_Id, (r, d) => new {Id = r.Inner, Name = d.Member_Name});
foreach (var child in children)
{
Console.WriteLine($"ID: {child.Id}, Name: {child.Name}");
}
}
public class ParentChildMap
{
public int parent_id;
public int child_id;
}
public class Member_Details
{
public int Member_Id;
public string Member_Name;
}
How to sort the given examples.
IEnumerable<extra> eList = new List<extra>()
{
new extra{ id = 1, text = "a"},
new extra{ id = 2, text = "g"},
new extra{ id = 3, text = "i"},
new extra{ id = 4, text = "e"},
new extra{ id = 5, text = "f"},
new extra{ id = 6, text = "d"},
new extra{ id = 7, text = "c"},
new extra{ id = 8, text = "h"},
new extra{ id = 9, text = "b"}
};
IEnumerable<sample> sam = new List<sample>()
{
new sample{ id = 1, name = "sample 1", list = new List<int>{1,5,6}},
new sample{ id = 2, name = "sample 1", list = new List<int>{2,9}},
new sample{ id = 3, name = "sample 1", list = new List<int>{8,3,7}},
new sample{ id = 4, name = "sample 1", list = new List<int>{3,4,8}},
new sample{ id = 5, name = "sample 1", list = new List<int>{1,5,7}},
new sample{ id = 6, name = "sample 1", list = new List<int>{6,9,7}}
};
I have this code to sort and join the sample list to the extra object above.
var s2 = (from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select new {
id = d2, text = e.text
}
).OrderBy(item => item.text.FirstOrDefault())
});
The code above works fine, it joined the two data and sorted the values for the list. But what I want is the output above 's2' will be sorted again by its 'list' value by 'list.text'.
So possible output above must be:
{ id = 1, name = "sample 1", list = {'a','f','d'}},
{ id = 5, name = "sample 1", list = {'a','f','c'}},
{ id = 2, name = "sample 1", list = {'g','b'}},
{ id = 4, name = "sample 1", list = {'i','e','h'}},
{ id = 6, name = "sample 1", list = {'d','b','c'}},
{ id = 3, name = "sample 1", list = {'h','i','c'}},
Is this possible in LINQ?
thanks
var newsam = sam.Select(s => new
{
id = s.id,
name = s.name,
list = s.list
.Select(l => eList.FirstOrDefault(e => e.id == l).text)
.OrderBy(e => e)
.ToList()
}
).OrderBy(s => s.list.FirstOrDefault())
.ToList();
EDIT
So, the inner lists are sorted by the text value of the eList; and the outer list is sorted by the first element of the inner list
EDIT
var s2=(from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select e.text
).OrderBy(item => item).ToList()
}).OrderBy(item => item.list.FirstOrDefault());