Is there any way to reduce the following code into Linq form?
foreach (var current in currentWhiteListApps)
{
var exists = false;
foreach (var whiteList in clientSideWhiteLists)
{
if (current.appID.Equals(whiteList.appID))
{
exists = true;
}
}
if (!exists)
{
deleteList.Add(current);
}
}
All I can think of is:
currentWhiteListApps.Select(x => {
var any = clientSideWhiteLists.Where(y => y.appID.Equals(x.appID));
if (any.Any())
deleteList.AddRange(any.ToArray());
return x;
});
Reason For LINQ
LINQ is far more readable than nested foreach loops, and requires less code. So this is the reason I would like it in LINQ
var deleteList = currentWhiteListApps.Where(x =>
clientSideWhiteLists.All(y => !x.appID.Equals(y.appID)))
.ToList();
var deleteList = currentWhiteListApps.Except(clientSideWhiteLists).ToList();
This solution assumes that both collections contains elements of the same type and this type has overriden Equals() that compares appID.
var validIds = new HashSet<int>(clientSideWhiteLists.Select(x => x.appId));
var deleteList = currentWhiteListApps.Where(x => !validIds.Contains(x.appId)).ToList();
Related
I am struggling with producing a sublist of objects with Linq.
var assignedObject = new List<int>();
foreach (var obj in myObjects) {
if (IsAssigned(obj)) {
assignedObject.add(obj.Id);
}
}
What I want to do now is remove assigned objects from myObjects with Linq .Where(Func<T,bool> predicate).
myObjects = myObject.Where(item => item.Id != assignedObj.All()).ToList();
item.Id != assignedObj.All() is the problem part.
If it is achievable with Linq please educate me.
Otherwise, I am going to implement a loop.
var remainingObjects = new List<MyClass>();
foreach(var obj in myObjects) {
if (!(assignedObjects.IndexOf(obj.Id) > -1)) {
remainingObjects.Add(obj);
}
}
PS: In case you're wondering List<MyClass> myObjects = GetMyObjects(params); which retrieved from somewhere else.
var toRemove = myObjects.Where(item=>assignedObj.Any(a=>a.Id == item.Id)).ToArray()
Does that work?
You can rewrite your All() with a condition, like this:
myObjects = myObject
.Where(item => assignedObj.All(assigned => item.Id != assigned.Id))
.ToList();
However, it is easier to use Contains:
var assignedIds = new HashSet<int>(assignedObject);
myObjects = myObject.Where(item => assignedIds.Contains(item.Id)).ToList();
Using HashSet<int> speeds up the lookup in situations when assignedObject is relatively large - say, hundreds of objects or more.
It can be like this: var list = from item in myObjects where IsAssigned(item) select item
Try this
var assignedObjects = myObjects.Where(m => IsAssigned(m)).ToList()
var remainedObjects = myObjects.Where(m => !IsAssigned(m)).ToList()
I'm constructing a linq query that will check is a string in the DB contains any of the strings in a list of strings.
Something like.
query = query.Where(x => x.tags
.Contains(--any of the items in my list of strings--));
I'd also like to know how many of the items in the list were matched.
Any help would be appreciated.
Update: I should have mentioned that tags is a string not a list. And I am adding on a couple more wheres that are not related to tags before the query actually runs. This is running against entity framework.
EDIT: This answer assumed that tags was a collection of strings...
It sounds like you might want:
var list = new List<string> { ... };
var query = query.Where(x => x.tags.Any(tag => list.Contains(tag));
Or:
var list = new List<string> { ... };
var query = query.Where(x => x.tags.Intersect(list).Any());
(If this is using LINQ to SQL or EF, you may find one works but the other doesn't. In just LINQ to Objects, both should work.)
To get the count, you'd need something like:
var result = query.Select(x => new { x, count = x.tags.Count(tag => list.Contains(tag)) })
.Where(pair => pair.count != 0);
Then each element of result is a pair of x (the item) and count (the number of matching tags).
I've done something like this before:
var myList = new List<string>();
myList.Add("One");
myList.Add("Two");
var matches = query.Where(x => myList.Any(y => x.tags.Contains(y)));
like this:
List<string> list = new List<string>();
list.Add("One");
list.Add("Two");
var result = query.Where(x => list.Contains(x.tags));
I am not quite sure from your question if x.tags is a string or list, if it is a list Jon Skeet's answer is correct. If I understand you correctly though x.tags is a string of strings. If so then the solution is:
list.Any(x => x.tags.IndexOf(x) > -1)
to count them do
list.Count(x => x.tags.IndexOf(x) > -1)
var t = new List<string> { "a", "b", "c" };
var y = "a b d";
var res = y.Count(x => t.Contains(x.ToString()));
I faced a similar problem recently and here's how I managed to work it out:
var list = [list of strings];
if (list != null && list.Any())
{
queryable = queryable.Where(x => x.tags != null);
var tagQueries = new List<IQueryable<WhateverTheDbModelIs>>();
foreach (var element in list)
{
tagQueries.Add(queryable.Where(x => x.tags.Contains(element)));
}
IQueryable<WhateverTheDbModelIs> query = tagQueries.FirstOrDefault();
foreach (var tagQuery in tagQueries)
{
query = query.Union(tagQuery);
}
queryable = queryable.Intersect(query);
}
probably not the best option but something a less experienced developer can understand and use
I'm having some trouble with finding the right syntax to accomplish the following:
Is it possible with LINQ (Lambda Expression) to .GroupBy data and instead of using the usual .Sum() or .Count() I want the resulting data to be a List of Int.
I defined my own class named: Filter_IDs. Its constructor needs two parameters:
public int? type; // Represents the object_type column from my database
public List<int?> objects; // Represents the object_id column from my database
I want to load data from my database into this object. The following LINQ query should result in a List of Filter_IDs:
The following LINQ query should result in a List of Filter_IDs:
List<Filter_IDs> filterids = ef.filterLine
.GroupBy(fl => fl.objectType)
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();
Using this query gives no building error but gives an 'NotSupportedException' on RunTime.
The database looks like this to give you a better understanding of the data:
http://d.pr/i/mnhq+ (droplr image)
Thanks in advance,
Gerben
I think the problem is the DB is not able to call ToList in the select, nor to create a new Filter_ID.
Try something like this :
List<Filter_IDs> filterids = ef.filterLine.Select(o => new { objectType = o.objectType, object_id=o.object_id})
.GroupBy(fl => fl.objectType).ToList()
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();
Maybe you want
IList<Filter_IDs> filterIds = ef.filterline
.Select(fl => fl.objectType).Distinct()
.Select(ot => new Filter_IDs
{
type = ot,
objects = ef.filterline
.Where(fl => fl.objectType == ot)
.Select(fl =>objectType)
.ToList()
}).ToList();
Get the distinct list objectType and use that to subquery for each list of object_id.
However, it seems more efficient to me to just enumerate the values in order,
var results = new List<Filter_IDs>();
var ids = new List<int>();
var first = true;
int thisType;
foreach (var fl in ef.filterLines
.OrderBy(fl => fl.objectType)
.ThenBy(fl => fl.object_Id))
{
if (first)
{
thisType = fl.objectType;
first = false;
}
else
{
if (fl.objectType == thisType)
{
ids.Add(fl.object_Id);
}
else
{
results.Add(new Filter_IDs
{
Type = thisType,
objects = ids
});
thisType = fl.objectType;
ids = new List<int>();
}
}
}
You can use GroupBy on client side:
List<Filter_IDs> filterids = ef.filterLine
.Select(fl=>new {fl.ObjectType, fl.object_id})
.AsEnumerable()
.GroupBy(fl => fl.objectType)
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();
I have 2 collections and i have the following code to loop through one collection and see if it exists in another collection. If it does exist, then update a property of that item.
foreach (var favorite in myFavoriteBooks)
{
var book = allBooks.Where(r => r.Name == favorite.Name).FirstOrDefault();
if (book != null)
{
book.IsFavorite = true;
}
}
Is there a more elegant or faster way to achieve this code above?
You can use Join for this, either in extension method syntax or LINQ syntax:
Extension method:
foreach(var favorite in myFavoriteBooks.Join(allBooks,
f => f.Name,
a => a.Name,
(f, a) => a))
{
a.IsFavorite = true;
}
LINQ:
var favorites = from f in myFavoriteBooks
join a in allBooks on f.Name equals a.Name
select a
foreach(var favorite in favorites)
{
favorite.IsFavorite = true;
}
These solutions are functionally identical; they differ only by syntax and they are faster than your original solution, since LINQ will build a hashtable on both sides and use that for matching rather than scanning the other list for every item in the original list.
You can more easily find the books in one collection that match another collection with Any
var booksInCommon = allBooks.Where(b => myFavoriteBooks.Any(bi => bi.Name == b.Name));
foreach(book b in booksInCommon)
b.IsFavorite = true;
Or, if you don't mind "tricky" code
allBooks.Where(b => myFavoriteBooks.Any(bi => bi.Name == b.Name)).ToList()
.ForEach(b => b.IsFavorite = true);
EDIT
As Adam Robinson points out, this is an O(N2) algorithm, so avoid it if you have thousands of books in both of your collections and opt for his Join answer.
If the books collections were stored in dictionaries with the key of Name, then this becomes an easy and very efficient operation:
var myFavoriteBooks = new System.Collections.Generic.Dictionary<string, book>();
var allBooks = new System.Collections.Generic.Dictionary<string, book>();
foreach (var bookName in myFavoriteBooks.Keys)
{
if (allBooks.ContainsKey(bookName))
{
allBooks[bookName].IsFavorite = true;
}
}
var favTitles = new HashSet<string>(favorite.Select(f => f.Name));
//favTitles now a hash-based O(1) name lookup.
foreach(var book in allBooks.Where(b => favTitles.Contains(b.Name)))
book.IsFavorite = true;
I am trying to gransp wether I can get big refactoring advantages out of learning LINQ.
How can LINQ improve this code, which is a real-world example that is representative for a lot of code in a project I work on:
foreach (SchemeElement elem in mainDiagram.Elements)
{
if (elem.SubType == EElemSubType.BusBar)
{
if (connPts.Busbars.ContainsKey(elem.ConnectionPointId))
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
} // if busbar
} // foreach element
For Clarity:
taAddrList.TAAddressList is of type Dictionary<ElemKey, TAAddress>
where ElemKey is a two-component type that consists of two int ID's.
connPts.Busbars is of type Dictionary<int, TAAddress>
See for yourself:
var query = from element in mainDiagram.Elements
where element.SubType == EElemSubType.BusBar
where connPts.Busbars.ContainsKey(element.ConnectionPointId)
select element;
foreach (var element in query)
{
// by accessing immidiatly in a dictionary (assuming you are using one), you can either insert or update
taAddrList.TAAddressList[element.Key] = connPts.Bushbars[elem.ConnectionPointId];
}
Well this depends, its certainly alot easier to write that sort of stuff in LINQ, but the depends part is on whether TAddressList is just a Dictionary... if it were you can get that dictionary easily:
var dictionary = mainDiagram.Elements.Where(e => e.SubType == EElemSubType.BusBar)
.ToDictionary(k => k.Key, e => connPts.BusBars[e.ConnectionPointId])
If you have to add to TAddressList in exactly the manner you gave in your example, you simply need to ForEach over the list
mainDiagram.Elements.Where(e => e.SubType == EElemSubType.BusBar && !taAddrList.TAAddressList.Contains(e.Key))
.ToList()
.ForEach(e => taAddrList.TAAddressList.Add(elem.Key, connPts.BusBars[e.ConnectionPointId]));
You can use linq for selecting a list of SchemeElement:
var query = from elem in mainDiagram.Elements
where elem.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(elem.ConnectionPointId)
select elem;
foreach (SchemeElement elem in query){
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
var elements =
mainDiagram.Elements
.Where(e => e.SubType == EElemSubType.BusBar &&
connPts.Busbars.ContainsKey(e.ConnectionPointId))
foreach (var elem in elements)
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
var items = mainDiagram.Elements
.Where(el => el.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(el.ConnectionPointId));
items.ForEach(item =>
{
if (!taAddrList.TAAddressList.ContainsKey(item.Key))
{
taAddrList.TAAddressList.Add(item.Key, new TAAddress());
}
});
foreach (SchemeElement elem in mainDiagram.Elements.Where(r => r.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(r.ConnectionPointId)))
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
} // foreach element
Code bellow is not tested, I assumed finally addresses want to go a specific Dictionary of Address objects, and address class contains two property, Key and value:
addressDic = mainDiagram.Elements.Where(x=>x.SubType == EElemSubType.BusBar)
.Where(x=>connPts.Busbars.ContainsKey(x.ConnectionPointId))
.GroupBy(x=>x.Key)
.Select(x=>new {Key = x.Key,
Value = connPts.Busbars[x.Last().ConnectionPointId]})
.ToDictionary(x=>x.Key);
but as you can see, It's not very readable in linq, but depend on your power in linq, may be it's simpler than for loop.