How to intersect list in c#? - c#

I have following list of Item objects in c#:
public class Item
{
public int Id { get; set; }
public List<string> Orders { get; set; }
}
List<Item> item = new List<Item>() {
new Item() { Id = 1, Code = 23, Orders = new List<string>() { "A", "B" }},
new Item() { Id = 2, Code = 24, Orders = new List<string>() { "C", "D" }},
new Item() { Id = 1, Code = 23, Orders = new List<string>() { "E", "F" }},
new Item() { Id = 3, Code = 25, Orders = new List<string>() { "G", "H" }}
};
I want to concat the Orders whose Id is same, so the output of above list should be:
{
new Item() { Id = 1, Code = 23, Orders = new List<string>() { 'A', 'B', 'E', 'F' },
new Item() { Id = 2, Code = 24, Orders = new List<string>() { 'C', 'D' },
new Item() { Id = 3, Code = 25, Orders = new List<string>() { 'G', 'H' }
};
How can i do this efficiently in c# ( using linq if possible ) ?

You want to group the items based on their ID, and then create a new sequences based on all of the Orders for that group.
var query = items.GroupBy(item => item.Id)
.Select(group => new Item
{
Id = group.Key,
Orders = group.SelectMany(item => item.Orders).ToList()
});
Note that this is not the intersection of any data. You're getting the union of all data within each group.

It appears what you want is something like this:
var output = items.GroupBy(i => i.Id)
.Select(g => new Item()
{
Id = g.Key
Orders = g.SelectMany(i => i.Orders)
.ToList()
});
Or in query syntax:
var output =
from i in items
group i by i.Id into g
select new Item()
{
Id = g.Key
Orders = g.SelectMany(i => i.Orders).ToList()
};

You can group your items by their id, then create new item for each id concatenate the orders:
item.GroupBy(x => x.Id)
.Select(x => new Item
{
Id = x.Key,
Orders = x.SelectMany(a => a.Orders).ToList()
}).ToList();

Related

add all values to a list of list from a list

I have a list of type class abc
public class abc
{
public int count;
public string country;
}
The list can have values like
Count: 1 - country: US Count: 2 - country: US Count: 3 -
country: IND Count: 4 - country: UK Count: 5 - country: UK
Now I want to put this list into a list of lists where it should be segregated based on countries.
My new list of lists should look like this
Count: 1 - country: US Count: 2 - country: US
Count: 3 - country: IND
Count: 4 - country: UK Count: 5 - country: UK
The count can have any integer and the country can have any string.
Is there any easy way to do this?
You can use GroupBy and select afterwards each group into a separate list:
List<abc> mylist = new List<abc>()
{
new abc{count = 1, country = "US"},
new abc{count = 2, country = "US"},
new abc{count = 3, country = "IND"},
new abc{count = 4, country = "UK"},
new abc{count = 5, country = "UK"},
};
List<List<abc>> result = mylist.GroupBy(x => x.country).Select(y => y.ToList()).ToList();
this way you get a list containing 3 other lists
Implement it like this:
List<abc> list = new List<abc>()
{
new abc() {country = "US", count = 1},
new abc() {country = "US", count = 2},
new abc() {country = "IND", count = 3},
new abc() {country = "UK", count = 4},
new abc() {country = "UK", count = 5}
};
Dictionary<string,List<abc>> dictionary = new Dictionary<string, List<abc>>();
foreach (var item in list)
{
if(!dictionary.TryGetValue(item.country,out var l))
{
l = new List<abc>();
dictionary.Add(item.country,l);
}
l.Add(item);
}
List<List<abc>> result = dictionary.Values.ToList();
you can do like this.
List<abc> ls = new List<abc>();
ls.Add(new abc() { count = 1, country = "US" });
ls.Add(new abc() { count = 2, country = "US" });
ls.Add(new abc() { count = 3, country = "IND" });
ls.Add(new abc() { count = 4, country = "UK" });
ls.Add(new abc() { count = 5, country = "UK" });
List<List<abc>> listOfList = new List<List<abc>>();
foreach (var group in ls.GroupBy(x => x.country))
{
List<abc> list = new List<abc>();
foreach (var item in group)
{
list.Add(new abc() { count = item.count, country = item.country });
}
listOfList.Add(list);
}
LINQ
List<List<abc>> listOfList = new List<List<abc>>();
foreach (var (group, list) in from item in ls.GroupBy(x => x.country)
let temp = new List<abc>()
select (item, temp))
{
foreach (var item2 in group)
{
list.Add(new abc() { count = item2.count, country = item2.country });
}
listOfList.Add(list);
}
Like many already answered, you could group your list by the countryString. However I personally would prefer to add it into a dictionary, so the access would be much easier to understand.
List<abc> myList = new List<abc>()
{
new abc{count = 1, country = "US"},
new abc{count = 2, country = "US"},
new abc{count = 3, country = "IND"},
new abc{count = 4, country = "UK"},
new abc{count = 5, country = "UK"},
};
You could as mentioned just group them:
var groupedLists = myList.GroupBy(x => x.country).Select(y => y.ToList()).ToList();
or you can make a dictionary out of it:
var myDictionary = myList.Select(item => item.country).Distinct().ToDictionary(country => country, country => myList.Where(item => item.country == country).ToList());
Now having the dictionary, you could access the specific list by the key (country). For example:
myDictionary["US"]; //would give you all items with the country "US"
It is up to you to chose whatever you would like to use. Just be aware that if you use the dictionary, you need to handle the possible keyNotFoundException
The easiest way to do this is with Linq.
You can use .GroupBy() to create groupings based on a properties value - in this case the Country.
In this example the .Select() statement uses a named tuple to make the data a bit more readable.
using System.Collections.Generic;
using System.Linq;
var data = new List<Abc>()
{
new()
{
Count = 1,
Country = "UK"
},
new()
{
Count = 2,
Country = "UK"
},
new()
{
Count = 3,
Country = "US"
}
};
var groupedData = data
.GroupBy(x => x.Country)
.Select(x => (Country: x.Key, Data: x.ToList()))
.ToList();
A way to consume and use this list of lists would be like so:
foreach (var (country, groupData) in groupedData)
{
var groupDataString = string.Join(" ", groupData.Select(x => x.Count));
Console.WriteLine($"{country}: {groupDataString}");
}
Example output looks like:
UK: 1 2
US: 3

group and combine items using LINQ

public class Product
{
public string Code { get; set; }
public string Name { get; set; }
public string Amount { get; set; }
}
public List<Product> Products = new List<Product>()
{
new Product() { Code="A1", Name="Vacuum", Amount = "10" },
new Product() { Code="A2", Name="Iron", Amount = "20" },
new Product() { Code="A3", Name="Kettle", Amount = "13" },
new Product() { Code="A2", Name="Microwave", Amount = "11" },
new Product() { Code="A3", Name="Dryer", Amount = "3" }
};
I need to select all products without duplicate code. Products with the same code should be combined into one line, in this case name and amount should be separated by commas. How to modify the following code to make it more elegant
var list1 = new List<Product>();
var gl = Products.GroupBy(x => x.Code).Where(x => x.Count() > 1);
gl.ToList().ForEach(x => list1.AddRange(x));
var list2 = Products.Where(x => !list1.Contains(x)).ToList(); // uniq values
var list3 = gl.Select(x =>
{
var p = new Product() { Code = x.Key };
p.Name = string.Join(",", x.ToList().Select(r => r.Name).Distinct());
p.Amount = string.Join(",", x.ToList().Select(r => r.Amount).Distinct());
return p;
}).ToList();
list2.AddRange(list3);
list2.ForEach(x =>
{
Console.WriteLine($"{x.Code.PadRight(20)},{x.Name.PadRight(20)},{x.Amount.PadRight(20)}");
});
the result should be:
Code Name Amount
A1 Vacuum 10
A2 Iron, Microwave 20, 11
A3 Kettle, Dryer 13, 3
Use GroupBy on Code, then iterate over it to get the individual elements to combine using string.Join().
var results = products
.GroupBy
(
p => p.Code
)
.Select
(
g => new
{
Code = g.Key,
Name = string.Join
(
",",
g.Select( p => p.Name )
),
Amount = string.Join
(
",",
g.Select( p => p.Amount.ToString() )
)
}
);
Output:
A1 Vacuum 10
A2 Iron,Microwave 20,11
A3 Kettle,Dryer 13,3
Link to working example on DotNetFiddle

How to merge group by list and sum no-key value

For example i have a grouped by list:
List<Cl> list = new List<Cl>()
{
new Cl() { vi = 1, vs = "q", am = 20 },
new Cl() { vi = 1, vs = "w", am = 30 },
new Cl() { vi = 2, vs = "w", am = 40 },
new Cl() { vi = 1, vs = "q", am = 50 }
};
groups = list.GroupBy(b => new { b.vi, b.vs }).ToList();
And i want to get merged list with summing am field:
Key am
[1, "q"] [70]
[1, "w"] [30]
[2, "w"] [40]
Is it possible to realize it without Dictionaries or KeyValuePair?
I will be grateful for any help.
First GroupBy(), then Sum() up the am values of each group
List<Cl> groups = list.GroupBy(b => new { b.vi, b.vs })
.Select(x => new Cl() { vi = x.Key.vi,
vs = x.Key.vs,
am = x.Sum(y => y.am) }).ToList();
You could try something like this:
class Key
{
public int Vi { get; set; }
public string Vs { get; set; }
public Key(int vi, string vs)
{
Vi = vi;
Vs = vs;
}
}
var result = groups = list.GroupBy(b => new { b.vi, b.vs })
.Select(gr=> new { Key = Key(gr.vi,gr.vs)
, Value = gr.Sum(x=>x.am)})
.ToList();
Try it like this:
var groups = list.GroupBy(b => new { b.vi, b.vs })
.Select(g => new { g.Key, am = g.Sum(s => s.am)})
.ToList();

Descending sorting of a list of object based on a count from a different class

I'm stuck on this problem where I need to do descending sort based on other list. l_lstNames need to update by age descending.
public class Test
{
public String Name;
public Int32 Age;
}
List<String> l_lstNames = new List<String> { "A1", "A3", "A2", "A4", "A0" };
List<Test> l_lstStudents = new List<Test>
{
new Test { Age = 33, Name = "A0" },
new Test { Age = 10, Name = "A1" },
new Test { Age = 50, Name = "A2" },
new Test { Age = 8, Name = "A3" },
new Test { Age = 25, Name = "A4" },
};
// Output
List<String> l_lstNames = new List<String> { "A2", "A0", "A4", "A1", "A3" };
Found few sames samples but not matching what I'm looking for. Thank you for help.
Create Dictionary<string, int> with Name to Age mapping and use it within order method:
var dict = students.ToDictionary(x => x.Name, x => x.Age);
var ordered = source.OrderByDescending(x => dict[x.Name]).ToList();
or you can just order students collection and then select Name only:
var ordered = students.OrderByDescending(x => x.Age)
.Select(x => x.Name)
.ToList();
If you just want the names in order descending:
var sorted = l_lstStudents // From the list of students
.OrderByDescending(l => l.Age) // with the oldest student first
.Select(s => s.Name) // give me just the names
.ToList(); // in a list!
I think this is what you are looking for
List<String> l_lstNames1 = (from student in l_lstStudents
where l_lstNames.Any(a => student.Name == a)
orderby student.Age descending
select student.Name ).ToList();
OR
List<String> l_lstNames2 = l_lstStudents.OrderByDescending(a => a.Age)
.Where(a => l_lstNames.Any(b => b == a.Name))
.Select(a => a.Name).ToList();

How can I add list parameters to a LINQ query?

I have two parameters passed to the method and I need to attach them to the final query list.
(1st parameter)
string[] Price= new string[5];
Price= new string[] { "50", "25", "35" };
(2nd parameter)
List<string> DiscountPrice= new List<string>();
DiscountPrice.Add ("10");
DiscountPrice.Add ("5");
DiscountPrice.Add ("3");
var list= (from d in context.List
where ....
select new MyNewList
{
Name = d.Name,
Country = d.Country,
**Price = ??** //how do I attach the parameters one by one? In the order they were saved?
**DiscountPrice** = ??
}).ToList<MyNewList>();
It sounds like you want to match list elements by index. You can iterate from zero to the number of list elements and access each element by its index:
var prices = new string[] { "50", "25", "35" };
var discountPrices = new List<string>() { "10", "5", "3" };
var items = (from d in context.List
where ....
select new { d.Name, d.Country }).ToList();
var list = (from index in Enumerable.Range(0, items.Count())
select new MyNewList
{
Name = items[index].Name,
Country = items[index].Country,
Price = prices[index],
DiscountPrice = discountPrices[index]
}).ToList();
Another way is to Zip everything together:
var list = items.Zip(prices, (item, price) => new { item, price })
.Zip(discountPrices, (x, discountPrice) => new { x.item, x.price, discountPrice})
.Select(x => new MyNewList
{
Name = x.item.Name,
Country = x.item.Country,
Price = x.price,
DiscountPrice = x.discountPrice
})
.ToList();

Categories