I have a list of class :
class GroupAssets
{
public string Name { get; set; }
public List<string> Assets { get; set; }
}
List<GroupAssets> GroupList2 = new List<GroupAssets>{
new GroupAssets { Name="Group1", Assets = new List<string>{ "A","B","C","D" }},
new GroupAssets { Name="Group1", Assets = new List<string>{ "A","B","E","F" }},
new GroupAssets { Name="Group3", Assets = new List<string>{ "A","B","H","G" }},
new GroupAssets { Name="Group4", Assets = new List<string>{ "A","I","C","J" }}
};
I would like to remove the duplicates and have this result :
Group1 => D
Group2 => E,F
Group3 => H,G
Group4 => I,J
Duplicate => A,B,C
Thank you for your help
List<GroupAssets> GroupList = new List<GroupAssets>{
new GroupAssets { Name="Group1", Assets = new List<string>{ "A","B","C","D" }},
new GroupAssets { Name="Group1", Assets = new List<string>{ "A","B","E","F" }},
new GroupAssets { Name="Group3", Assets = new List<string>{ "A","B","H","G" }},
new GroupAssets { Name="Group4", Assets = new List<string>{ "A","I","C","J" }}
};
var assetList = new Dictionary<string,int>();
foreach (var g in GroupList.Select(x=> x.Assets)) {
g.ForEach(x=> {
if (!assetList.ContainsKey(x)) assetList.Add(x,1);
else assetList[x]++;
});
}
var nonUnique = assetList.Where(x=> x.Value > 1).Select(x=> x.Key).ToList();
nonUnique.ForEach(x=> { Console.WriteLine(x); });
alternative solution in case you want to know the total amount of duplicates
I assume you made a mistake and property GroupAssets.Assets contains the list of assets (new List<string>() {"A", "B"}) and not the list of comma separated strings with only one string in the list (new List<string>() {"A,B"}).
First you have to figure out what are the duplicates. You could group items by one of strings "A" to "J" and the value int is the number of occurrences of that key in all lists. We take code from another Stack Overflow question, enhanced with one SelectMany because we want to flatten many lists into one.
var assetCount = GroupList
.SelectMany(x => x.Assets)
.GroupBy(x => x)
.Select(s => new { Asset = s.Key, Count = s.Count() });
Then we make list of duplicates, and a list of groups with unique assets:
var duplicates = assetCount.Where(x => x.Count > 1).Select(x => x.Asset).ToList();
var uniqueAssetsGroupList = GroupList
.Select(x => new GroupAssets() { Name = x.Name, Assets = x.Assets.Except(duplicates).ToList() });
foreach (var group in uniqueAssetsGroupList)
Console.WriteLine(string.Format("{0} => {1}", group.Name, string.Join(",", group.Assets)));
Console.WriteLine("Duplicate => {0}", string.Join(",", duplicates));
Assuming that you have
class GroupAssets
{
public String Name { get; set; }
public IList Assets { get; set; }
}
List<GroupAssets> GroupList = new List<GroupAssets>{
new GroupAssets { Name="Group1", Assets = new List<string>{ "A" ,"B", "C", "D" }},
new GroupAssets { Name="Group2", Assets = new List<string>{ "A" ,"B", "E", "F" }},
new GroupAssets { Name="Group3", Assets = new List<string>{ "A" ,"B", "H", "G" }},
new GroupAssets { Name="Group4", Assets = new List<string>{ "A" ,"I", "C", "J" }},
};
note, that each Asset has 4 items (not 1) you can put
Code:
HashSet<string> duplicates = new HashSet<string>();
HashSet<string> all = new HashSet<string>();
foreach (var item in GroupList)
foreach (var asset in item.Assets)
if (!all.Add(asset)) // duplicate if all contains the asset
duplicates.Add(asset);
// removing duplicates from each Asset
foreach (var item in GroupList)
item.Assets.RemoveAll(item => duplicates.Contains(item));
Let's have a look:
string report = string.Join(Environment.NewLine, GroupList
.Select(item => $"{item.Name} => {string.Join(", ", item.Assets)}"));
Console.WriteLine(report);
Console.WriteLine("Duplicate => {string.Join(", ", duplicates)}");
Outcome:
Group1 => D
Group2 => E, F
Group3 => H, G
Group4 => I, J
Duplicate => A, B, C
If, however, each of Assets contains 1 comma separated item, you should add Split and Join:
HashSet<string> duplicates = new HashSet<string>();
HashSet<string> all = new HashSet<string>();
foreach (var item in GroupList)
foreach (var asset in item.Assets.SelectMany(list => list.Split(',')))
if (!all.Add(asset))
duplicates.Add(asset);
foreach (var item in GroupList) {
item.Assets = item
.Assets
.Select(asset => asset.Split(',').Where(c => !duplicates.Contains(c)))
.Where(asset => asset.Any())
.Select(asset => string.Join(",", asset))
.ToList();
}
find the unique duplicates and then use except to remove the duplicates from the asset list
[Fact]
public void TestRemoveDuplicate()
{
List<GroupAssets> GroupList = new List<GroupAssets>{
new GroupAssets { Name="Group1", Assets = new List<string>{ "A" ,"B", "C", "D" }},
new GroupAssets { Name="Group2", Assets = new List<string>{ "A" ,"B", "E", "F" }},
new GroupAssets { Name="Group3", Assets = new List<string>{ "A" ,"B", "H", "G" }},
new GroupAssets { Name="Group4", Assets = new List<string>{ "A" ,"I", "C", "J" }},
};
IList<String> duplicates = new List<String>();
foreach (var item in GroupList)
{
foreach (var element in item.Assets)
{
if (GroupList.Where(e =>e.Name!=item.Name && e.Assets.Contains(element)).Any())
{
if (duplicates.Contains(element) == false) { duplicates.Add(element); }
}
}
}
foreach (var item in GroupList)
{
item.Assets = item.Assets.Except(duplicates).ToList();
string result = "";
foreach (var element in item.Assets)
{
result += element + " ";
}
_output.WriteLine($"Name: {item.Name} Asset: {result}");
}
Assert.True(duplicates.Count() > 0);
}
output:
Name: Group1 Asset: D
Name: Group2 Asset: E F
Name: Group3 Asset: H G
Name: Group4 Asset: I J
List<string> tempList = new List<string>();
Dictionary<string, int> keyValuePairs = new Disctionary<string, int>();
GroupList.ForEach(x => {
tempList.AddRange(x.Assets);
});
tempList.ForEach(X => {
if(!keyValuePairs.Keys.Contains(x))
{
keyValuePairs.Add(x,1);
}
else
{
keyValuePairs[x]++;
}
});
tempList.Clear();
tempList.AddRange(keyValuePairs.Where(x => x.Value > 1).Select(x => x.Key));
GroupList.ForEach(x => {
var temp = x.Assets;
x.Assets = temp.Except(tempList).ToList();
});
Related
List<string> OngoingClass = new List<String>()
{
"FLEXI","RETAIL","LITE","RTSPL"
};
List<string> ReturnClass = new List<String>()
{
"FLEXI","LITE","RETAIL","RTSPL"
};
Need to Combine those Two List with Certain Conditions.
1 . OngoingClass FLEXI Should Combine with ReturnClass FLEXI - Should not combine with rest of the classes.
2. Same way LITE fare should combine with LITE Fare.
3. Rest of the classes can combine each other.
For Example Result would like
{ "FLEXI" , "FLEXI" }
{ "LITE","LITE"}
{ "RETAIL","RETAIL"}
{ "RETAIL","RTSPL"}
{ "RTSPL","RETAIL"}
{ "RTSPL","RETAIL"}
As I said in comments, There could be better algorithms to get some combinations, but fo this small dataset this will do the trick
List<Tuple<string, string>> resultSequence = new List<Tuple<string, string>>();
OngoingClass.ForEach( item =>
{
ReturnClass.ForEach( item2 =>
{
if ((item == item2 && item2 == "FLEXI") || (item == item2 && item2 == "LITE") || ( item != "FLEXI" && item != "LITE" && item2 != "FLEXI" && item2 != "LITE"))
{
resultSequence.Add( new Tuple<string, string> ( item, item2 ));
}
});
});
Show result
foreach (var item in resultSequence)
{
Console.WriteLine(item);
}
This works for me:
var OngoingClass = new List<String>() { "FLEXI", "RETAIL", "LITE", "RTSPL" };
var ReturnClass = new List<String>() { "FLEXI", "LITE", "RETAIL", "RTSPL" };
var SpecialClass = new List<String>() { "FLEXI", "LITE" };
var query =
from o in OngoingClass
from r in ReturnClass
where (SpecialClass.Contains(o) && r == o)
|| (!SpecialClass.Contains(o) && !SpecialClass.Contains(r))
select (o, r);
This allows any values to be "special" and only pair with themselves.
I get this result:
Here is an approach that serves the purpose if you don't need/want duplicates. The "special" matches and other matches are aggregated separately, before the results are concatenated into a list of tuples (List<(string, string)>).
var OngoingClass = new List<String>() { "FLEXI","RETAIL","LITE","RTSPL" };
var ReturnClass = new List<String>() { "FLEXI","LITE","RETAIL","RTSPL" };
var SpecialClass = new List<String> { "FLEXI", "LITE" };
var SpecialResult =
OngoingClass.Intersect(ReturnClass).Intersect(SpecialClass)
.Select(special => (special, special));
var OtherResult =
OngoingClass.Except(SpecialClass)
.SelectMany(ongoing => ReturnClass.Except(SpecialClass)
.Select(ret => (ongoing, ret)));
var Result = SpecialResult.Concat(OtherResult).ToList();
Result:
[0]: ("FLEXI", "FLEXI")
[1]: ("LITE", "LITE")
[2]: ("RETAIL", "RETAIL")
[3]: ("RETAIL", "RTSPL")
[4]: ("RTSPL", "RETAIL")
[5]: ("RTSPL", "RTSPL")
Try following :
List<string> OngoingClass = new List<String>() { "FLEXI","LITE","RETAIL","RTSPL"};
List<string> ReturnClass = new List<String>() { "FLEXI","LITE","RETAIL","RTSPL"}
var results = OngoingClass.Select((x,i) => new object[] { x, ReturnClass[i]}).ToArray();
There is a similar question that doesn't answer my question. --> Count number of element in List>
I have a list which contains sublists:
List<string> sublist1 = new List<string>() { "a", "b" };
List<string> sublist2 = new List<string>() { "a", "b" };
List<string> sublist3 = new List<string>() { "a", "c" };
Now I want to count the occurrences of each list.
a, b --> 2
a, c --> 1
I used distinct() from LINQ, but I got the output:
a, b --> 1
a, b --> 1
a, c --> 1
I assume that the hashcode is different.
Is there an alternative to distinct() which is looking at the list values instead?
I want to solve this in LINQ if possible.
Edit:
The order of list items has to be the same!
To use GroupBy() to do this, you will need a suitable IEqualityComparer<List<string>> that compares lists of strings. There is no built-in implementation, so you have to roll your own:
public sealed class StringListEqualityComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
return x.SequenceEqual(y);
}
public int GetHashCode(List<string> strings)
{
int hash = 17;
foreach (var s in strings)
{
unchecked
{
hash = hash * 23 + s?.GetHashCode() ?? 0;
}
}
return hash;
}
}
Once you've got that, you can use it with GroupBy() as follows:
public static void Main()
{
var sublist1 = new List<string>{ "a", "b" };
var sublist2 = new List<string>{ "a", "b" };
var sublist3 = new List<string>{ "a", "c" };
var listOfLists = new List<List<string>> {sublist1, sublist2, sublist3};
var groups = listOfLists.GroupBy(item => item, new StringListEqualityComparer());
foreach (var group in groups)
{
Console.WriteLine($"Group: {string.Join(", ", group.Key)}, Count: {group.Count()}");
}
}
public JsonResult CountList(){
List<List<string>> d = new List<List<string>>(); //SuperList
d.Add(new List<string> { "a", "b" }); //List 1
d.Add(new List<string> { "a", "b" }); // List 2
d.Add(new List<string> { "a", "c" }); // List 3
d.Add(new List<string> { "a", "c", "z" }); //List 4
var listCount = from items in d
group items by items.Aggregate((a,b)=>a+""+b) into groups
select new { groups.Key, Count = groups.Count() };
return new JsonResult(listCount);
}
This will give the following Result as output in Post Man or Advanced REST Client
[{
"key": "ab",
"count": 2
},
{
"key": "ac",
"count": 1
},
{
"key": "acz",
"count": 1
}],
I think this will be helpful
var list = new List<List<string>>() { sublist1, sublist2, sublist3};
var result = list.GroupBy(x => string.Join(",",x)).ToDictionary(x => x.Key.Split(',').ToList(), x => x.Count());
You can try the below code:-
List<string> sublist1 = new List<string>() { "a", "b" };
List<string> sublist2 = new List<string>() { "a", "b" };
List<string> sublist3 = new List<string>() { "a", "c" };
List<List<string>> listOfLists = new List<List<string>> { sublist1, sublist2, sublist3 };
Dictionary<string, int> counterDictionary = new Dictionary<string, int>();
foreach (List<string> strList in listOfLists)
{
string concat = strList.Aggregate((s1, s2) => s1 + ", " + s2);
if (!counterDictionary.ContainsKey(concat))
counterDictionary.Add(concat, 1);
else
counterDictionary[concat] = counterDictionary[concat] + 1;
}
foreach (KeyValuePair<string, int> keyValue in counterDictionary)
{
Console.WriteLine(keyValue.Key + "=>" + keyValue.Value);
}
I think I will solve this with:
var equallists = list1.SequenceEqual(list2);
Therefore I compare distinct lists and lists with SequenceEquals() and counting them.
Better solutions welcome. :)
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();
This is my collection
var list1 = new List<string> { "aa", "bb", "cc", "dd", "ee" }
.Select(x => new Pair { PropName = x, TypeName = x + "Prop1" })
.ToList();
var list2 = new List<string> { "aa", "bb2", "cc", "dd", "ee" }
.Select(x => new Pair { PropName = x, TypeName = x + "Prop2" })
.ToList();
var list3 = new List<string> { "aa", "bb", "cc", "dd" }
.Select(x => new Pair { PropName = x, TypeName = x + "Prop3" })
.ToList();
var list4 = new List<string> { "aa" }
.Select(x => new Pair { PropName = x, TypeName = x + "Prop4" })
.ToList();
var list5 = new List<string> { "aa", "bb", "bb5", "cc5" }
.Select(x => new Pair { PropName = x, TypeName = x + "Prop5" })
.ToList();
var collection = new List<List<Pair>> { list1, list2, list3, list4, list5 };
It represents some hierarcys (i.e. "aa" is father for "bb")
I want to make something like tree with "Pair" objects. It should have structure like on picture. But with Pair objects. And I do not have any Idea how to do that.
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();