Combining Two list with Certail Conditions Using Linq - c#

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();

Related

How to remove duplicates in List of Class with Linq

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();
});

LINQ, to check if this characters contains in my some tables in my DB

var unos = new List<string> { "W", "w" };
var pesme = _entities.Tracks.Where(x => unos.Contains(x.Name) || unos.Contains(x.Genre.Name) || unos.Contains(x.Album.Title) || unos.Contains(x.Album.Artist.Name)).ToList();
foreach (var p in pesme) {
Console.WriteLine($"{p.Name}-{p.Album.Title}-{p.Album.Artist.Name}-{p.Genre.Name}");
}
Actually you need to check Name contains W or w. But now you are checking W contains x.Name!!
Try this one:
var unos = new List<string> { "W", "w" };
var pesme = _entities.Tracks.ToList().Where(x => unos.Any(t=> x.Name.Contains(t) || x.Genre.Name.Contains(t) /* rest of conditions*/ )).ToList();
foreach (var p in pesme) {
Console.WriteLine($"{p.Name}-{p.Album.Title}-{p.Album.Artist.Name}-{p.Genre.Name}");
}

Find duplicate in a list from a reference list

I'd like know if at least one element of listRef is present more than once in listA ? The other values can be present more than once.
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" };
List<string> listRef = new List<string> { "B", "D" };
Thanks,
Try this:
bool hasRef = listref.Any(r => listA.Count(a => a == r) > 1);
I would use ToLookup method to generate Lookup<string, string> first, and then use it to check your condition:
var lookup = listA.ToLookup(x => x);
return listRef.Any(x => lookup.Contains(x) && lookup[x].Count() > 1);
You could use GroupBy and ToDictionary to achieve the same:
var groups = listA.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
return listRef.Any(x => groups.ContainsKey(x) && groups[x] > 1);
something like this
var query = listRef.Where(x=>
listA.Where(a => a == x)
.Skip(1)
.Any());
listRef.ForEach(refEl => {
var count = listA.Count(aEl => aEl == refEl);
if(count > 1) {
//Do something
}
});
Finding the best performing option in this case is not simple because that depends on the number of items in the lists and the expected result.
Here's a way to do it that is performant in the face of big lists:
var appearances = listA.GroupBy(s => s)
.Where(g => g.Count() > 1)
.ToDictionary(g => g.Key, g => g.Count());
var hasItemAppearingMoreThanOnce = listRef.Any(r => appearances.ContainsKey(r));
this works
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" };
List<string> listRef = new List<string> { "A", "D" };
foreach (var item in listRef)
{
if (listA.Where(x => x.Equals(item)).Count() > 1)
{
//item is present more than once
}
}
this can be another way to do
List<string> listA = new List<string> { "A", "A", "B", "C", "D", "E" , "D" };
List<string> listRef = new List<string> { "B", "D" };
var duplicates = listA.GroupBy(s => s).SelectMany(grp => grp.Skip(1));
var newData = duplicates.Select(i => i.ToString()).Intersect(listRef);
var result = listA.GroupBy(x=>x)
.Where(g=>g.Count()>1&&listRef.Contains(g.Key))
.Select(x=>x.First());
bool a = result.Any();
If the second list is large and can contain duplicates i would use a HashSet<string> and IntersectWith to remove possible duplicates and strings which are not in the first list from the second:
var refSet = new HashSet<string>(listRef);
refSet.IntersectWith(listA);
bool anyMoreThanOne = refSet.Any(rs => listA.ContainsMoreThanOnce(rs, StringComparison.OrdinalIgnoreCase));
Here the extension which is not very elegant but works:
public static bool ContainsMoreThanOnce(this IEnumerable<string> coll, String value, StringComparison comparer)
{
if (coll == null) throw new ArgumentNullException("col");
bool contains = false;
foreach (string str in coll)
{
if (String.Compare(value, str, comparer) == 0)
{
if (contains)
return true;
else
contains = true;
}
}
return false;
}
DEMO
However, if the second listRef isn't large or doesn't contain duplicates you can just use:
bool anyMoreThanOne = listRef
.Any(rs => listA.ContainsMoreThanOnce(rs, StringComparison.OrdinalIgnoreCase));

How to search through multiple sets with one linq statement

Consider three different lists of strings, one of which is a list of lists. I need to search them all to find a particular one.
In this sample, the result is achieved, but I would like to do it in one Linq statement. Note that I do not want to change the existing collections, nor create any new ones.
var collectionA = new List<string>() {"Item1", "Item2"};
var collectionB = new List<string>() { "Item3", "Item4" };
var listOfLists = new List<List<string>>() {new List<string>() {"Item5", "Item6"}, new List<string>(){ "Item7", "Item8"}};
//Is there a better Linq way to do this?
var searchString = "Item5";
var item = collectionA.FirstOrDefault(i => i == searchString);
if (item == null)
{
item = collectionB.FirstOrDefault(i => i == searchString);
if (item == null)
{
foreach (var listOfList in listOfLists)
{
item = listOfList.FirstOrDefault(i => i == searchString);
if (item != null)
{
break;
}
}
}
}
bool result = listOfLists.SelectMany(x => x)
.Concat(collectionA)
.Concat(collectionB)
.Any(x => x == "Item5");
var result = collectionA
.Concat(collectionB)
.Concat(listOfLists.SelectMany(i => i))
.FirstOrDefault(i => i == searchString);
You can use SelectMany to flatten list of list, add collectionA and collectionA to listOfLists first:
listOfLists.AddRange(new[] {collectionA, collectionB});
if (listOfLists.SelectMany(x => x).Any(y => y == "Item5"))
{
}
With you new edit which does not prefer to change the existing collections, nor create the new one, you can do:
if (listOfLists.Any(x => x.Any(y => y == "Item5"))
|| collectionA.Any(x => x == "Item5")
|| collectionB.Any(x => x == "Item5"))
{
}
Maybe this can help:
var collectionA = new List<string>() { "Item1", "Item2" };
var collectionB = new List<string>() { "Item3", "Item4" };
var listOfLists = new List<List<string>>() { new List<string>() { "Item5", "Item6" }, new List<string>() { "Item7", "Item8" } };
var val = from y in (from x in listOfLists[0] select x) where y == "Item5" select y;
you can modify more to get your expected result

Manipulation on all list items options for n lists in c#

I'd like to split my data to lists by an attribute's value and check all the combination options between the lists' items.
My problems are that I don't know how many list I'll get and if there is a better way to do that beside this way:
var a = Data.Descendants("value").Where(x => x.Attribute("v").Value == "1").ToList();
var b = Data.Descendants("value").Where(x => x.Attribute("v").Value == "2").ToList();
var c = Data.Descendants("value").Where(x => x.Attribute("v").Value == "3").ToList();
foreach (var tempA in a)
{
foreach (var tempB in b)
{
foreach (var tempC in c)
{
DO SOMETHING;
}
}
}
EDIT:
I'd like to check my items from one data source (var items = new List<string>{"1","1","2","3","2","1","3","3","2"})
Now I'd like to split this list to 3 lists (list a = "1","1","1" - list b = "2","2","2" - list c = "3","3","3")
In this step what I'm trying to do is to check all the combination from one item in one list to the other items in the other lists.
a[0] with b[0] c[0]
a[0] with b[0] c[1]
a[0] with b[0] c[2]
a[0] with b[1] c[0]
.
.
b[1] with a[2] c[2]
.
.
Thanks!
Could you try using the LINQ GroupBy method? Some examples are here:
LINQ GroupBy examples
You can use GroupBy to group your elements. Then you can create combinations using Linq.
var grouping = Data.Descendants("value")
.GroupBy(x => x.Attribute("v").Value);
var combinations grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
foreach(var c in combinations)
{
//Do Something
}
e.g.
public class Pair
{
public string A { get; set; }
public string B { get; set; }
}
var pairs = new List<Pair>();
pairs.Add(new Pair { A = "1", B = "2" });
pairs.Add(new Pair { A = "1", B = "3" });
pairs.Add(new Pair { A = "1", B = "4" });
pairs.Add(new Pair { A = "2", B = "1" });
pairs.Add(new Pair { A = "2", B = "2" });
pairs.Add(new Pair { A = "2", B = "3" });
var grouping = pairs.GroupBy(x => x.A);
var combinations = grouping.SelectMany(x =>
grouping.Select(y =>
new { Group = x, Combination = y }));
You can do this,following the line of thinking of romoku and chrisC
//new list of lists to hold new information.
List<List<Descendants>> NewList = new List<List<Descendants>>();
foreach (var item in Data.Descendants.GroupBy(x => x.Attribute("v").Value))
{
NewList.Add(item.ToList());
}
For your new edit list of Strings this will do it
List<List<string>> NewList = new List<List<string>>();
foreach (var item in OriginalList.GroupBy(x => x))
{
NewList.Add(item.ToList());
}

Categories