How to search through multiple sets with one linq statement - c#

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

Related

Combining Two list with Certail Conditions Using Linq

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

How can I change the value of a property in a list for just one value when the property value is ()?

I have a list in C# like this where in some rows, one of the fields sometimes has an entry of "()":
I can find these rows like this:
var ps = phraseSources.Where(x => x.WatsonMeaning == "()");
But how can I change using Linq, all occurrences of "()" in the WatsonMeaning column to "n/a".
Just use ForEach LINQ method:
list
.Where(i => i.WatsonMeaning == "()")
.ToList()
.ForEach(i => i.WatsonMeaning = "n/a")
Try this:
static void Test()
{
var phraseSources = new List<A>();
phraseSources.Add(new A { WatsonMeaning = "()" });
phraseSources.Add(new A { WatsonMeaning = "text1" });
phraseSources.Add(new A { WatsonMeaning = "()" });
phraseSources.Add(new A { WatsonMeaning = "text2" });
phraseSources.Add(new A { WatsonMeaning = "text3" });
phraseSources.Where(x => x.WatsonMeaning == "()")
.ToList()
.ForEach(x => x.WatsonMeaning = "n/a");
foreach ( var item in phraseSources )
Console.WriteLine(item.WatsonMeaning);
}
class A
{
public string WatsonMeaning;
}

Best algorithm to determine added and removed items when comparing to collections

I am looking for the best algorithm to compare 2 collections and determine which element got added and which element got removed.
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's for you to know what useful data is inside UserInvolvement.
//Both are Dictionary<int, int>, because The Involvement is just a enum flag. Integer. UserId is also Integer.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
//I Want to compare new to old -> and get 2 dictionaries: added and removed.
var usersAdded = new Dictionary<int, Involvement>();
var usersRemoved = new Dictionary<int, Involvement>();
//What is the best algoritm to accomplish this?
return GetInvolvementLogging(usersAdded, usersRemoved);
}
private string GetInvolvementLogging(Dictionary<int, Involvement> usersAdded, Dictionary<int, Involvement> usersRemoved)
{
//TODO: generate a string based on those dictionaries.
return "Change in userinvolvement: ";
}
Added elements are only in newDict removed only in oldDict
var intersection = newDict.Keys.Intersect(oldDict.Keys);
var added = newDict.Keys.Except(intersection);
var removed = oldDict.Keys.Except(intersection);
EDIT
I modify your base function, dictionaries is no neded.
Example UserInvolvement implementation
class UserInvolvement
{
public int UserId;
public string Name;
public string OtherInfo;
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
UserInvolvement p = obj as UserInvolvement;
if ((System.Object)p == null)
{
return false;
}
return (UserId == p.UserId) && (Name == p.Name) && (OtherInfo == p.OtherInfo);
}
public override string ToString()
{
return $"{UserId} - {Name} - {OtherInfo}";
}
}
And example function:
private static string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement,
ICollection<UserInvolvement> oldInvolvement)
{
var intersection = newInvolvement.Select(x => x.UserId).Intersect(oldInvolvement.Select(x => x.UserId));
var addedIds = newInvolvement.Select(x => x.UserId).Except(intersection);
var removedIds = oldInvolvement.Select(x => x.UserId).Except(intersection);
List<UserInvolvement> modifiedUI = new List<UserInvolvement>();
foreach (var i in intersection)
{
var ni = newInvolvement.First(a => a.UserId == i);
var oi = oldInvolvement.First(a => a.UserId == i);
if (!ni.Equals(oi))
{
modifiedUI.Add(ni);
}
}
List<UserInvolvement> addedUI = newInvolvement.Where(x => addedIds.Contains(x.UserId)).Select(w => w).ToList();
List<UserInvolvement> removedUI = oldInvolvement.Where(x => removedIds.Contains(x.UserId)).Select(w => w).ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Added");
foreach (var added in addedUI)
{
sb.AppendLine(added.ToString());
}
sb.AppendLine("Removed");
foreach (var removed in removedUI)
{
sb.AppendLine(removed.ToString());
}
sb.AppendLine("Modified");
foreach (var modified in modifiedUI)
{
sb.AppendLine(modified.ToString());
}
return sb.ToString();
}
And my test function:
static void Main(string[] args)
{
List<UserInvolvement> newUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 1,
Name = "AAA",
OtherInfo = "QQQ"
},
new UserInvolvement()
{
UserId = 2,
Name = "BBB",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
List<UserInvolvement> oldUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 2,
Name = "BBBC",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 3,
Name = "CCC",
OtherInfo = "QQ44"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
string resp = GetInvolvementLogging(newUI, oldUI);
WriteLine(resp);
ReadKey();
WriteLine("CU");
}
Result is:
Added
1 - AAA - QQQ
Removed
3 - CCC - QQ44
Modified
2 - BBB - 123
You could try with Linq:
var usersAdded = newDict.Except(oldDict);
var usersRemoved = oldDict.Except(newDict);
If you need dictionaries as a result you can cast:
var usersAdded = newDict.Except(oldDict).ToDictionary(x => x.Key, x => x.Value);
var usersRemoved = oldDict.Except(newDict).ToDictionary(x => x.Key, x => x.Value);
Think best algorithm will be
foreach (var newItem in newDict)
if (!oldDict.ContainsKey(newItem.Key) || oldDict[newItem.Key]!=newItem.Value)
usersAdded.Add(newItem.Key, newItem.Value);
foreach (var oldItem in oldDict)
if (!newDict.ContainsKey(oldItem.Key) || newDict[oldItem.Key]!=oldItem.Value)
usersRemoved.Add(oldItem.Key, oldItem.Value);
Finally this is my implementation of GetInvolvementLogging:
(the implementation of the string builder method is irrelevant for my question here)
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's to focus on the relevant data inside UserInvolvement.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var intersection = newDict.Keys.Intersect(oldDict.Keys); //These are the id's of the users that were and remain involved.
var usersAdded = newDict.Keys.Except(intersection);
var usersRemoved = oldDict.Keys.Except(intersection);
var addedInvolvement = newDict.Where(x => usersAdded.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
var removedInvolvement = oldDict.Where(x => usersRemoved.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
//Check if the already involved users have a changed involvement.
foreach(var userId in intersection)
{
var newInvolvementFlags = newDict[userId];
var oldInvolvementFlags = oldDict[userId];
if ((int)newInvolvementFlags != (int)oldInvolvementFlags)
{
var xor = newInvolvementFlags ^ oldInvolvementFlags;
var added = newInvolvementFlags & xor;
var removed = oldInvolvementFlags & xor;
if (added != 0)
{
addedInvolvement.Add(userId, added);
}
if (removed != 0)
{
removedInvolvement.Add(userId, removed);
}
}
}
return GetInvolvementLogging(addedInvolvement, removedInvolvement);
}

How to sort linq with fixed values and show all the rest with another sorting [duplicate]

This question already has answers here:
LINQ OrderBy versus ThenBy
(4 answers)
Closed 5 years ago.
IQueryable<Employee> query = ((IEnumerable<Employee>)employeeList)
.Select(x => x)
.AsQueryable();
var strListEmployees = input.MustIncludeIdsInPage.Split(",").ToList();
//the list of employee is dynamic, it'd return 3, 4, 5 or more data
var entities = query
.OrderBy(item => strListEmployees.IndexOf(item.Id.ToString()))
.PageBy(input)
.ToList();
example data
What I want is something like this in order:
by employee name
D
F
A
B
C
E
G
H
Employee D, F, A on top (fix value in List) and show the rest with name sorting (order by).
As M. Wiśnicki mentioned, this is easily solveable as You got only 3 elements. But to dynamically resolve this, I would stick to some function, where You would enter the List (or IEnumerable) of the objects and also the Names, based on which You want to filter them.
The code below is recursion, which will go through the array and select the 1st element (from array) and add the rest. Rest is calling the same function without the 1st name & without the element we have already added.
Something like:
public IEnumerable<Employee> GetOrderedPrefered(IEnumerable<Employee> aList, string[] aNames)
{
if (aNames.Length == 0) return aList.OrderBy(a => a.Name).ToList();
var lRes = new List<Employee>()
{
aList.FirstOrDefault(a => a.Name == aNames[0])
};
lRes.AddRange(
GetOrderedPrefered(
aList.Where(a => a.Name != aNames[0]),
aNames.Where(a => a != aNames.First()
).ToArray()
));
return lRes;
}
Usage:
var lRes = GetOrderedPrefered(persons, names);
foreach (var item in lRes)
Console.WriteLine(item.Name);
> D
> F
> A
> B
> C
> E
> G
You can use OrderBy() and ThenBy()
List<Test> tests = new List<Test>()
{
new Test() {EmployeeID = "1", Name = "A"},
new Test() {EmployeeID = "2", Name = "B"},
new Test() {EmployeeID = "3", Name = "C"},
new Test() {EmployeeID = "4", Name = "D"},
new Test() {EmployeeID = "5", Name = "E"},
new Test() {EmployeeID = "6", Name = "F"},
new Test() {EmployeeID = "7", Name = "G"},
new Test() {EmployeeID = "8", Name = "H"},
};
var x = tests.OrderBy(name => name.Name != "D")
.ThenBy(name => name.Name != "F")
.ThenBy(name => name.Name != "A")
.ThenBy(name => name.Name)
.ToList();
Result is: First D,F,A and others names
Edit:
string[] filtr = new[] {"D", "F", "A"};
var fdata = tests.Where(d => filtr.Contains(d.Name)).OrderBy(z=>z.Name).ToList();
var odata = tests.Where(d => !filtr.Contains(d.Name)).OrderBy(z => z.Name).ToList();
fdata.AddRange(odata);
var set = Enumerable.Range(0, 8)
.Select(i => new {
Name = new string(new[] { (char)('A' + i) })
});
var before = string.Join(",", set.Select(i => i.Name)); //A,B,C,D,E,F,G,H
var priorities = "D,F".Split(',').Select((v, i) => new { Value = v, Index = i });
var query = from s in set
join p in priorities on s.Name equals p.Value into m
from x in m.DefaultIfEmpty(new { Value = s.Name, Index = int.MaxValue })
orderby x.Index, s.Name
select s.Name;
var result = string.Join(",", query); //D,F,A,B,C,E,G,H

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

Categories