C# merge multiple lists by id and summing the property - c#

I got a grapic class which looks like this:
public class Graphic
{
public int id { get; set; }
public int Counter { get; set; }
}
and now i have 3 lists of grapic:
public void myFunction()
{
List<Graphic> l1= new List<Graphic> ();
List<Graphic> l2= new List<Graphic> ();
List<Graphic> l3= new List<Graphic> ();
l1.Add(new Graphic {id = 1, Counter = 4});
l1.Add(new Graphic {id = 2, Counter = 2});
l1.Add(new Graphic {id = 3, Counter = 9});
l1.Add(new Graphic {id = 4, Counter = 1});
l2.Add(new Graphic {id = 1, Counter = 1});
l2.Add(new Graphic {id = 2, Counter = 2});
l2.Add(new Graphic {id = 3, Counter = 3});
l2.Add(new Graphic {id = 5, Counter = 1});
l3.Add(new Graphic {id = 1, Counter = 12});
l3.Add(new Graphic {id = 7, Counter = 1});
l3.Add(new Graphic {id = 8, Counter = 3});
l3.Add(new Graphic {id = 4, Counter = 1});
}
And i need a code which will merge these 3 lists, each graphic with it's ID and the counter property will be summed up, so the result will be:
var mergedList = {
Graphic {id = 1, Counter = 17},
Graphic {id = 2, Counter = 4},
Graphic {id = 3, Counter = 12},
Graphic {id = 4, Counter = 2},
Graphic {id = 5, Counter = 1},
Graphic {id = 7, Counter = 1},
Graphic {id = 8, Counter = 3}
}
any idea will help me.
Is there a way to do that efficiently with LINQ?

Use .Concat to merge the 3 lists and then GroupBy to get all the Counter values of the different ids:
var result = l1.Concat(l2)
.Concat(l3)
.GroupBy(item => new { item.id, item.Geometry }, item => item.Counter)
.Select(group => new Graphic
{
id = group.Key,
Counter = group.Sum()
}).ToList();
See collection initializer for a neater way to initialize the lists:
List<Graphic> l1 = new List<Graphic>
{
new Graphic { id = 2, Counter = 2 },
new Graphic { id = 3, Counter = 9 },
new Graphic { id = 4, Counter = 1 },
new Graphic { id = 1, Counter = 4 },
};
I also suggest reading C# naming conventions for the naming of your function and the properties.

Related

How to merge 2 listitems

I have 2 listitems
First
{Id = 1, Name = "a"}{Id = 2, Name = "b"}{Id = 3, Name = "c"}
Second
{Id = 2, Name = "b"}{Id = 3, Name = "c"}{Id = 4, Name = "d"}
how to merge them and get a listitem like:
{Id = 1, Name = "a"}{Id = 2, Name = "b"}{Id = 3, Name = "c"}{Id = 4, Name = "d"}
Something like this ?
class items {
public int id;
public string name;
}
static void Main(string[] args)
{
var list = new List<items>();
list.Add(new items {id=1 , name="a"});
list.Add(new items { id = 2, name = "b" });
list.Add(new items { id = 3, name = "c" });
var list1 = new List<items>();
list1.Add(new items { id = 2, name = "b" });
list1.Add(new items { id = 3, name = "c" });
list1.Add(new items { id = 4, name = "d" });
var c = list.Select(b=> new {b.id, b.name});
var d = list1.Select(b => new { b.id, b.name });
var merge = c.Concat(d).Distinct().ToList();
foreach (var item in merge)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
var list1 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
var list2 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
list1.AddRange(list2);
Then list1 is the merged list.
You can use the "AddRange" or "Union" or "Concat" functions of c#
e.g
var list1 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
var list2 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
then
var MergedResult = list1.Union(list2).ToList();
or
var MergedResult = list1.AddRange(list2).ToList();
also you can use Concat as well...
see the difference amongst them here .NET List Concat vs AddRange

Linq Join: Join using a mapping table and associate objects

I am trying to write a linq query to make a very basic join. I have two arrays
Park[] parks = new Park[]{
new Park() {ID = 1, Name = "Free Park"},
new Park() {ID = 2, Name = "Cost Park"},
new Park() {ID = 3, Name="Sneak in Park"}
};
and
Facility[] facilities = new Facility[] {
new Facility() { ID = 1, Name = "Swing", MinimumAge = 1, MaximumAge = 120},
new Facility() { ID = 2, Name = "Slide", MinimumAge = 1, MaximumAge = 200},
new Facility() { ID = 3, Name = "See-Saw", MinimumAge = 1, MaximumAge = 300}
};
Each park can have 0...n facilities, hence we have a set of mapping objects
ParkFacility[] associations = new ParkFacility[] {
new ParkFacility() {ParkID = 1, FacilityID = 1},
new ParkFacility() {ParkID = 1, FacilityID = 2},
new ParkFacility() {ParkID = 1, FacilityID = 3},
new ParkFacility() {ParkID = 2, FacilityID = 1},
new ParkFacility() {ParkID = 3, FacilityID = 2}
};
This is the definition of the class Park
class Park
{
public int ID { get; set; }
public String Name { get; set; }
public Facility[] Facilities { get; set; }
}
Is it possible to use only joins and associate the appropriate facilities to the parks? i.e. set the Facilities array in Park to be those appropriately mapped using the associations?
Edit: My research thus far..
var x_temp = from g in parks
join j in associations on g.ID equals j.ParkID into h
select new Park()
{
Name = g.Name,
ID = g.ID,
Facilities = (from u in h join m in facilities on u.FacilityID equals m.ID select m).ToArray()
};
I tried using a sub-linq query and it works, but I am looking for a solution with only joins
You can create a lookup from park id to facilities, and use this to populate the Facilities property on each of your Park objects. Note that the best place to do this is in the constructor of Park, but in keeping with your existing code this snippet will do it in the object initializer:
var lookup = associations.ToLookup(pf => pf.ParkID, pf => facilities.Single(f => f.ID == pf.FacilityID));
Park[] parks = new Park[]{
new Park() {ID = 1, Name = "Free Park", Facilities = lookup[1].ToArray()},
new Park() {ID = 2, Name = "Cost Park", Facilities = lookup[2].ToArray()},
new Park() {ID = 3, Name="Sneak in Park", Facilities = lookup[3].ToArray()}
};
Additionally, it would be helpful to store all your Facility and Park instances in a Dictionary mapping the ID to the instance. In that case your lookup wouldn't need to do a linear scan through the all the facilities for each association.

Sorting nested Lists within object using c# Lambda Expressions

I have an object
TestCollection testCollection = new TestCollection()
{
new Test
{
quickTest =
new QuickTest
{
id = a,
sortField = 3,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 1},
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 3},
new NestedQuickTest{sortField = 4},
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = b,
sortField = 2,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 21},
new NestedQuickTest{sortField = 32},
new NestedQuickTest{sortField = 11},
new NestedQuickTest{sortField = 2},
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = c,
sortField = 1,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 3},
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 8},
new NestedQuickTest{sortField = 1},
}
}
},
};
1) I would like to sort this object using lambda expressions.
2) I would like to get back the object sorted by the first in Asc order and then by the in Asc order.
3) I would like to remove the last property in both nested List<> objects so there are only two objects in each.
I apologize if this is a little confusing but I would like to do something like this:
var sorted = testCollection.OrderBy(x => x.quickTest.sortField).ThenBy(y => y.quickTest.nestedQuickTest.Select(z => z.sortField)).Take(2);
The end result would be:
TestCollection testCollection = new TestCollection()
{
,
new Test
{
quickTest =
new QuickTest
{
id = c,
sortField = 1,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 1},
new NestedQuickTest{sortField = 2}
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = b,
sortField = 2,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 11}
}
}
}
};
Thanks in advance for your help, I can't seem to get my desired results and I know I am missing something obvious.
If I understand what you are saying you want to order the objects in MyOrderList by the property with the NestedObject, which is a collection of objects who should themselves ordered by the value of the AnotherNestedObject, which is also a list.
Well first of all you need to implement a Icomparer or something like that to set the rules of what makes one list bigger than the other.
See, you want to order objects in a list by a value, if its an int, well thats easy, but being a list, what are the rules to determine which one is bigger? Is it the one with more elements? The one with biggest sum of elements?
You need to figure out this first
EDIT:
Okay I think I understand your question better, you just want to order the outer lists objects by the int, and then also have the list inside each of those elements ordered too.
I think it would be easier if you did this in 2 steps something like:
var orderedTestCollection = testCollection.Orderby(tc=>tc.quickTest.SortField) // orders all the testCollection objects
//orders and only takes 2 elements of the lists inside the each test collection object
foreach(var tc in testCollection)
{
tc.quickTest.NestedQuickTest = tc.quickTest.NestedQuickTest.Orderby(nqt=>nqt.SortField).Take(2);
}

Easiest way to create a nested list from flat data

Assume I have the following information in a flat format, for example a list of objects:
List<Line> lines = new List<Line>
{
new Line{Id = 1, Level = 0},
new Line{Id = 2, Level = 1},
new Line{Id = 3, Level = 1},
new Line{Id = 4, Level = 2},
new Line{Id = 5, Level = 2},
new Line{Id = 6, Level = 1},
new Line{Id = 7, Level = 1},
new Line{Id = 8, Level = 2},
new Line{Id = 9, Level = 1}
};
Each object has an id and a level. What I want to end up with a nested list. For this I have a class which can have a list of children, based on the level.
public class NestedLine
{
public int Id;
public List<NestedLine> Children = new List<NestedLine>();
}
What's the easiest way to convert that flat list into a nested list?
EDIT: The only information on how to construct the list is the order of the lines and the level. This should be the result:
1
--2
--3
--4
--5
--6
--7
--8
--9
Here is my attempt.
Calling code
List<Line> lines = new List<Line>
{
new Line{Id = 1, Level = 0},
new Line{Id = 2, Level = 1},
new Line{Id = 3, Level = 1},
new Line{Id = 4, Level = 2},
new Line{Id = 5, Level = 2},
new Line{Id = 6, Level = 1},
new Line{Id = 7, Level = 1},
new Line{Id = 8, Level = 2},
new Line{Id = 9, Level = 1}
};
NestedLine nestedLine = Recusrsive(lines, 0);
Recursive Method
private NestedLine Recusrsive(List<Line> lines, int listPos)
{
NestedLine retVal = new NestedLine();
Line line = lines[listPos];
retVal.Id = line.Id;
for (int iItem = listPos + 1; iItem < lines.Count; iItem++)
if (lines[iItem].Level == line.Level + 1)
retVal.Children.Add(Recusrsive(lines, iItem));
else if (lines[iItem].Level <= line.Level) return retVal;
return retVal;
}
Can you provide a little more information here? This looks to me like you are really dealing with a tree structure:
1
-> 2
-> 3
-> -> 4
-> -> 5
-> 6
-> 7
-> -> 8
-> 9
am i correct? and if so, what is the rule for determining the parent of the lower level nodes?
The first thing that comes to mind is to use a recursive constructor for your NestedLine class:
public NestedLine(List lines)
Use a stack to keep track what was the last item added at each level, pushing and popping as you go along:
var stack = new Stack<NestedLine>();
foreach (var line : lines) {
while (stack.Count > line.Level) {
// Pop items until the top element is one up from current level
stack.Pop()
}
var child = new NestedLine{Id = line.Id};
if (stack.Count > 0) {
// if there is a parent, add the child to its children
stack.Peek().Children.Add(child);
}
// add current line as the deepest item
stack.Push(child);
}
NestedLine root;
while (stack.Count) {
root = stack.Pop();
}
Disclaimer: I haven't actually done much C# programming so this might contain some trivial errors.

What is the easiest way to fixed pivot transform this data in C# (linq?)?

Given this example data (in .NET classes where Po, Sku, Qty are properties):
PO, Sku, Qty
1,ABC,1
1,DEF,2
1,GHI,1
1,QWE,1
1,ASD,1
1,ZXC,5
1,ERT,1
2,QWE,1
2,ASD,11
2,ZXC,1
3,ERT,1
3,DFG,1
3,DFH,1
3,CVB,4
3,VBN,1
3,NMY,1
I need to transform it into a fixed column format, with a max of 5 SKUs per line (repeating the PO if needed for > 5):
PO, SkuA, QtyA, SkuB, QtyB, SkuC, QtyC, SkuD, QtyD, SkuE, QtyE
1, ABC, 1, DEF, 2, GHI, 1, QWE, 1, ASD, 1
1, ZXC, 5, ERT, 1, , , , , ,
2, QWE, 1, ASD, 11, ZXC, 1, , , ,
3, ERT, 1, DFG, 1, DFH, 1, CVB, 4, VBN, 1
3, NMY, 1, , , , , , , ,
Output can be CSV (which is what I'm outputting), or .NET classes - no matter there. Is there a simple way to do this in Linq by grouping by PO, then by counts of 5?
EDIT: I have no control of over the destination format. And for anyone interested, it's VendorNet and VendorBridge that require this nonsense.
Firstly, here's the query that will generate the correct hierarchy of objects. I'm using anonymous types but it's easy enough to change it to use your own proper classes.
var query = yourData
.GroupBy
(
x => x.PO
)
.SelectMany
(
x => x.Select
(
(y, i) => new { y.PO, y.Sku, y.Qty, Key = i / 5 }
)
)
.GroupBy
(
x => new { x.PO, x.Key }
);
Using LINQ to create the CSV from the query results is bit of a hack, but it gets the job done. (The "benefit" of using LINQ is that you could chain the original query and the CSV generation into a single, massive statement, should you wish.)
IEnumerable<string> csvLines = query
.Select
(
x => x.Aggregate
(
new { Count = 0, SB = new StringBuilder() },
(a, y) => new
{
Count = a.Count + 1,
SB = ((a.SB.Length == 0) ? a.SB.Append(y.PO) : a.SB)
.Append(", ").Append(y.Sku).Append(", ").Append(y.Qty)
},
a => a.SB.ToString() + string.Join(", , ", new string[6 - a.Count])
)
);
string csv = string.Join(Environment.NewLine, csvLines.ToArray());
In my opinion, creating the CSV without using LINQ makes the code much more readable:
StringBuilder sb = new StringBuilder();
foreach (var group in query)
{
int count = 0;
foreach (var item in group)
{
if (count++ == 0)
{
sb.Append(item.PO);
}
sb.Append(", ").Append(item.Sku).Append(", ").Append(item.Qty);
}
while (count++ < 5)
{
sb.Append(", , ");
}
sb.Append(Environment.NewLine);
}
string csv = sb.ToString();
Here you go. I didn't format the output the way you wanted. But this should give you an idea of how to pivot rows. Hope this helps :-)
public class MyClass
{
public int PO { get; set; }
public String SKU { get; set; }
public int Qty { get; set; }
public static IEnumerable<MyClass> GetList()
{
return new List<MyClass>()
{
new MyClass {PO = 1, SKU = "ABC", Qty = 1},
new MyClass {PO = 1, SKU = "DEF", Qty = 2},
new MyClass {PO = 1, SKU = "GHI", Qty = 1},
new MyClass {PO = 1, SKU = "QWE", Qty = 1},
new MyClass {PO = 1, SKU = "ASD", Qty = 1},
new MyClass {PO = 1, SKU = "ZXC", Qty = 5},
new MyClass {PO = 1, SKU = "ERT", Qty = 1},
new MyClass {PO = 2, SKU = "QWE", Qty = 1},
new MyClass {PO = 2, SKU = "ASD", Qty = 1},
new MyClass {PO = 2, SKU = "ZXC", Qty = 5},
};
}
}
EDIT: I've fixed the query based on Luke's comment
var lQuery =
MyClass.GetList()
.GroupBy(pArg => pArg.PO)
.Select(pArg => new
{
Test = pArg.Select((pArg1, pId) =>
new {ID = (pId / 5),
pArg1.PO, pArg1.SKU, pArg1.Qty})
.GroupBy(pArg1 => pArg1.ID)
.Select(pArg1 =>
pArg1.Aggregate(pArg.Key.ToString(),
(pSeed, pCur) =>
pSeed + pCur.SKU + ","))
});

Categories