I have a list of objects each containing a list of strings, and I want to use group objects that contain the same value within their lists.
I'm trying something like this, but it puts them all in the same lists.
List<Item> Items = new List<Item>();
Item Item1 = new Item();
Item1.Id = "1";
Item1.Names.AddRange(new List<string> { "A" });
Items.Add(Item1);
Item Item2 = new Item();
Item2.Id = "2";
Item2.Names.AddRange(new List<string> { "B" });
Items.Add(Item2);
Item Item3 = new Item();
Item3.Id = "3";
Item3.Names.AddRange(new List<string> { "A", "C" });
Items.Add(Item3);
List<List<Item>> GroupedItems = Items.GroupBy(u => u.Names.GroupBy(s => s)).Select(t => t.ToList()).ToList();
What I would like to have happen in this scenario is GroupedItems contains two lists, the first contains Item1and Item3, the second contains Item2.
List<List<Item>> GroupedItems = Items
// Select only distinct names that can occur
.SelectMany(x => x.Names).Distinct()
// For every unique name find items that have it in Names
.Select(name => Items.Where(x => x.Names.Contains(name)).ToList())
.ToList();
Related
I have two list
List<string> listA;
List<string> listB;
How to get next item of listA when im iterating with listB? Pseudocode:
List<dynamic> listC = new List<dynamic>();
foreach (var elementA in listA)
{
listC.Add(new
{
a: elementA,
b: listB.TakeNextItem() // how to achive this?
});
}
You could use Enumerable.Zip:
var listC = listA
.Zip(listB, (a, b) => new { a, b })
.ToList();
This iterates over both lists and projects items into a new list.
It also statically types your listC variable, rather than using dynamic.
You could use a for loop instead of a foreach loop and then use the current index to access both listA and listB at the same time.
List<dynamic> listC = new List<dynamic>();
// When listA Count is as bigger as listB don't execute for-loop
if (listA.Count > listB.Count) {
return;
}
for (int i = 0; i < listA.Count; i++) {
listC.Add(new {
a = listA[i],
b = listB[i]
});
}
want to check if list contains same items
var listme = new List<string>();
listme.Add("list1");
listme.Add("list1");
And want to count the number of same items and copy it and then remove it from list.
You can do it in a single LINQ statement with GroupBy and ToDictionary:
var dupCounts = listme
.GroupBy(s => s)
.Where(g => g.Count() > 1) // Keep only groups with duplicates
.ToDictionary(g => g.Key, g => g.Count());
This produces a Dictionary<string,int> where each item from the list is mapped to its corresponding count from the original list. Now you can remove all duplicates from the original list:
listme.RemoveAll(dupCounts.Keys);
Try to use HashTable instead of List
Hashtable hashtable = new Hashtable();
hashtable[1] = "One";
hashtable[2] = "Two";
hashtable[13] = "Thirteen"
You can use linq, see below:
public static void Main()
{
var listme = new List<string> {"A", "A", "B", "C", "C"};
// count
var countDict = listme.GroupBy(i => i)
.ToDictionary(i => i.Key, i => i.Count());
foreach (var kv in countDict)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// remove
listme.RemoveAll(s => s == "A");
foreach (string s in listme)
{
Console.WriteLine(s);
}
Console.ReadLine();
}
I have 2 lists.
They are different in length but same type.
I want that an Item from List2 replaces an equal item in List1.
var item1 = new Item { Id = 1, Name = "Test1" };
var item2 = new Item { Id = 2, Name = "Test2" };
var item3 = new Item { Id = 3, Name = "Test3" };
var item4 = new Item { Id = 4, Name = "Test4" };
var item5 = new Item { Id = 5, Name = "Test5" };
var list1 = new List<Item> { item1, item2, item3, item4, item5 };
var list2 = new List<Item> { new Item { Id = 1, Name = "NewValue" } };
As a result I expect a list with 5 items where the item with Id = 1 has a value "NewValue".
How can I do that preferable with linq.
UPDATE
I extend my question:
How can the replacement of the replaced Item happen without copying all properties manually. Just imagine I have 100 properties...
This is one way to do it:
First define an equality comparer that depends only on the Id property of the Item class like this:
public class IdBasedItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Id == y.Id;
}
public int GetHashCode(Item obj)
{
return obj.Id.GetHashCode();
}
}
Then you can take items list1 that don't have corresponding items in list2 using the Except method and then you can concatenate that with list2 using the Concat method like this:
var result = list1.Except(list2, new IdBasedItemEqualityComparer()).Concat(list2).ToList();
Notice how I use the IdBasedItemEqualityComparer with the Except method, so that comparison is based only on Id.
Off the top of my head this is one solution
var list3 = new List<Item>();
foreach (var item in list1)
list3.Add(list2.FirstOrDefault(s => s.Id == item.Id) ?? item);
I think LEFT OUTER JOIN in Linq will be able to merge 2 lists regardless of number of properties(columns) like this:
List<Item> newItems =
(from l1 in list1
join l2 in list2 on l1.Id equals l2.Id into l12
from l2 in l12.DefaultIfEmpty()
select new { Item = (l2 == null) ? l1 : l2 }).Select(r => r.Item).ToList();
IList<MyObject> ob1 = {new MyObject {Id = "1", Items = {BananaObject1, BananaObject2}}}
IList<MyObject> ob2 = { new MyObject {Id = "1", Items = {BananaObject2, BananaObject3}},
new MyObject {Id = "2", Items = {BananaObject3, BananaObject3}}}
I want to merge the 2 lists such that the resulting list would be
IList<MyObject> ob2 = { new MyObject {Id = "1", Items = {BananaObject1, BananaObject2, BananaObject3}},
new MyObject {Id = "2", Items = {BananaObject3, BananaObject3}}}
So since the id of the first item of the 2 lists were the same, they became one, and one of their property is concatenated.
I can do a for loop to achieve this, but I am looking for a neat best performance linq expression for this.
thank you
Concat the lists together, GroupBy Id property, SelectMany to get the merged list of items:
ob1.Concat(ob2)
.GroupBy(o => o.Id)
.Select(g => new MyObject()
{
Id = g.Key,
Items = g.SelectMany(o => o.Items ).Distinct().ToList()
});
Use MoreLINQ:
obj1.FullGroupJoin(obj2,
a=>a.Id,
b=>b.Id,
(id,a,b)=>new {id=id,Items=a.Items.Union(b.Items)},
new {id=-1, Items=new List<string>()}, //Default for left side
new {id=-2, Items=new List<string>()});//Default for right side
I have multiple lists of strings, IList<string>, that I want to consolidate in one list showing distinct string and count for each item (like a dictionary). what is the most efficient way to do this?
LINQ (certainly the most efficient in terms of code to type and maintain; the overall performance will be about the same as any other approach):
if the lists are in separate variables:
var qry = from s in listA.Concat(listB).Concat(listC) // etc
group s by s into tmp
select new { Item = tmp.Key, Count = tmp.Count() };
if the lists are all in a parent list (of lists):
var qry = from list in lists
from s in list
group s by s into tmp
select new { Item = tmp.Key, Count = tmp.Count() };
Then if you really want a list:
var resultList = qry.ToList();
Dictionary<string, int> count = new Dictionary<string, int>();
foreach(IList<int> list in lists)
foreach(int item in list) {
int value;
if (count.TryGetValue(item, out value))
count[item] = value + 1;
else
count[item] = 1;
}
List<List<string>> source = GetLists();
//
Dictionary<string, int> result = source
.SelectMany(sublist => sublist)
.GroupBy(s => s)
.ToDictionary(g => g.Key, g => g.Count())