How to convert a query to bool?
I used the "ALL (x => x)" but did not give the answer I needed.
Code Line
checkItemInventory.Where(x => listCost.Contains(x.Id));
In this case, the listcost would have 2 items, I needed to check if the checkItemInventory has these 2 items.
"All items in the inventory have an id that present in listcost". listCost needs to have the same number of items as inventory (assuming Id is unique) possibly more, to stand a chance of returning true
checkItemInventory.All(x => listCost.Contains(x.Id))
"At least one item in the inventory has an id that is also in listCost". Listcost could minimally have only one id in it, to stand a chance of returning true
checkItemInventory.Any(x => listCost.Contains(x.Id))
As you can see, neither of these are what you want as you seem to be saying you want to check whether every item in listcost is also present in the inventory. This is like the top code, but the other way round ("all items in listCost are present in inventory" vs "all items in inventory are present in listcost"
I think I'd make a dictionary out of the inventory first, unless it's already something that supports a fast lookup:
var d = checkItemInventory.Select(x => new { x.Id, x.Id }).ToDictionary();
var boolResult = listCost.All(lc => d.ContainsKey(lc));
If inventory is small, you could use this approach:
listCost.All(lc => checkItemInventory.Any(cii => cii.Id == lc));
Just be mindful that internally it might do something like:
bool all = true;
foreach(lc in listCost){
bool found = false;
foreach(cci in checkItemInventory)
if(lc == cci.Id){
found = true;
break;
}
all &= found;
if(!all)
return false;
}
return true;
Which is a lot of repeated comparisons (for every item in listCost, the whole inventory is scanned), could be slow
Edit
I asked for clarification of how you store your inventory and your costs of building items. Here's one assumption I made, and how a solutio based on it might work:
Assuming your inventory has the kind of item and a count saying how many of that item the player is carrying:
class InventoryItem{
int ItemKindId { get; set;}
int CountOf { get; set; }
}
player.Inventory.Add(new InventoryItem() {
ItemKindId = Constants.WOOD, //1
CountOf = 10 //holding 10 items of wood
};
player.Inventory.Add(new InventoryItem() {
ItemKindId = Constants.STONE, //2
CountOf = 5 //holding 5 items of stone
};
Assuming you have a Recipe for making e.g. an axe, it needs 1 wood and 2 stone, but it lists them in simple order:
int[] axeRecipe = new int[] { Constants.WOOD, Constants.STONE, Constants.STONE };
Might be easiest to group the recipe:
var recipe = axeRecipe.GroupBy(item => item)
/*
now we have a grouping of the recipe[item].Key as the material and a
recipe[item].Count() of how much. The group is like a dictionary:
recipe[Constants.WOOD] = new List<int>{ Constants.WOOD };
recipe[Constants.STONE] = new List<int>{ Constants.STONE, Constants.STONE, };
A group item has a Key and a list of objects that have that key
Because my recipe was simply ints, the Key is the same number as all the
items in the list
*/
//for all items in the recipe
grp.All(groupItem =>
//does the player inventory contain any item
playerInventory.Any(inventoryItem =>
//where the material kind is the same as the recipe key (material)
inventoryItem.ItemKindId == groupItem.Key &&
//and the count they have of it, is enough to make the recipe
inventoryItem.CountOf >= groupItem.Count()
);
You can of course reduce this to a single line if you want: axeRecipe.GroupBy(...).All(...)
You could map the listCost to a list of int and then use Except() and Any() to check whether all items are contained:
bool containsAll = !listCost.Select(x => x.Id).Except(checkItemInventory).Any();
[UPDATE]
You are telling us the following:
How to convert a query to bool? I used the "ALL (x => x)" but did not give the answer I needed.
checkItemInventory.Where(x => listCost.Contains(x.Id));
In this case, the listcost would have 2 items, I needed to check if
the checkItemInventory has these 2 items.
if you need to check if there is any result then you can use:
bool hasItems = checkItemInventory.Where(x => listCost.Contains(x.Id)).Any();
if you need to count the result you can use
checkItemInventory.Where(x => listCost.Contains(x.Id)).Count();
You could use a Join to create a method based Linq query and use the results to check if the length of the list is greater than 0. Then turn that into a boolean.
var query = checkItemInventory.Join(listCost,
inventory => inventory.Id,
cost => cost.Id,
(inventory, cost) => new { id = inventory.Id });
var count = query.ToList().Count();
var b = (count > 0);
If I get it correctly, listCost can have less elements than checkItemInventory. You want to check that all elements in listCost have a corresponding element in checkItemInventory. Correct? If yes, try this:
listCost.All(x => checkItemInventory.Contains(x));
I don't know the type of these lists, so you might need to use x.id in some places
Related
I've researched and found that I use .Contains on the array to filter an entity, but my resulting list is empty what is wrong?
public List<Order> GetOrderstoShip()
{
var shipvia = new string[] { "UPS", "FED", "EX", "USP" };
var orders = db.Orders.Where(
x => x.VOID != "Y" && x.GONEDATE == "")
.ToList();
var exporders = orders.Where(
x => shipvia.Contains(x.SHIPVIA.ToUpper())
).ToList();
// Problem: exporders contains 0 items
// My database field contains the following:
// FEDEX, UPS, USPS, FEDEX GND, FEDEXGND
// And, I made sure orders has a list of 900+ items
return exporders;
}
New Development: I went into my DB and edited one record. I set the ShipVia field exaclty to UPS (previously it was UPS GND) and I got one item back in exporder.
That means the .Contains is exact match. So how do I get it to do something similar to the SQL Like?
I thought the whole point of using contains was that is does partial match.
Just a take a stab. You might have white space in your database table. You can try to trim the results first.
shipvia.Contains(x.SHIPVIA.Trim().ToUpper())
I would suggest doing this filter before the results are returned. This will speed up the database query time.
I had to use a work-around since I only had three strings to check for, I expanded my filter by using or... not the most elegant but it lets me continue with my project!
If anyone figures out why the .Contains is not working, please let me know...
public List<Order> GetOrderstoShip()
{
var shipvia = new string[] { "UPS", "FED", "EX", "USP" };
var orders = db.Orders.Where(
x => x.VOID != "Y" && x.GONEDATE == "" && (
x.SHIPVIA.Contains("UPS") ||
x.SHIPVIA.Contains("FED") ||
x.SHIPVIA.Contains("USP"))
).ToList();
var exporders = orders.Where(
x => shipvia.Contains(x.SHIPVIA.Trim().ToUpper())
).ToList();
// Problem: exporders contains 0 items
// My database field contains the following:
// FEDEX, UPS, USPS, FEDEX GND, FEDEXGND
// And, I made sure orders has a list of 900+ items
return orders;
}
I'm trying to find a neat way to find all of the values in an observable collection which meet a certain criteria. For this example to keep things simple lets say its the collection contains ints and I'm trying to find all of the items that are greater than 5.
The best way I currently know of doing it is like this
ObservableCollection<Int> findAllGreaterThanFive (ObservableCollection<Int> numbers)
{
ObservableCollection<Int> numbersGreaterThanFive;
foreach(Int number in numbers)
{
if (number > 5)
{
numbersGreaterThanFive.add(number);
}
}
return numbersGreaterThanFive;
}
Obviously ignore any simple solutions that take advantage to the fact I'm looking for ints I need a solution that works with any an ObservableCollection of any type with any condition. I was just wondering if checking every item with the foreach loop and the conditional is the best way of doing it?
You can say something like:
var numbersGreaterThanFive = numbers.Where(x => x > 5);
you can use System.Linq namespace, add using statement using System.Linq and after that you can use following Where method.
ObservableCollection<int> list = new ObservableCollection<int>();
list.Where(i => i > 5).ToList();
you can use any kind of objects like :
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
list.Where(i => i.ID > 10);
The code above returns DataItem's with ID greater than 10.
If you sure about there's only one record satisfying condition, you can use First() method like :
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
list.First(i => i.ID == 10);
Above code returns the DataItem with ID 10. But if there's no record with ID = 10 then it will throw an exception. Avoid of this if you're not sure there's only one record satisfies the condition. Also you can use FirstOrDefault() method.
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
DataItem item = list.FirstOrDefault(i => i.ID == 10);
if(item != null)
{
//DoWork
}
If there's no record with ID = 10, then item will be null.
EDIT, just saying the comment in the ContainsAllItem explains best.
Im sorry for asking, I know this was asked before, but I just did not get it.
Ok, so I want to check If a list contains all the items in another list WITHOUT overlapping, aswell as compare the items based on the classes string, name variable(called itemname and it is public).
public class Item
{
public string itemname;
}
So basically, have a class(lets say.. class A) with a list of items, and a function that checks takes the list of items of class A, and then compares it to another list(lets call it B), but compare it by the itemname variable not the whole item.
And most importantly could you explain in detail what it does.
So how the function/class would look as of now.
public class SomeClass
{
public List<Item> myItems = new List<Item>();
public bool ContainsAllItems(List<Item> B)
{
//Make a function that compares the to lists by itemname and only returns true if the myItems list contains ALL, items in list b.
//Also could you explain how it works.
}
}
I haven't checked the pref on this, but linq does have the Except operator.
var x = new int[] {4,5};
var y = new int[] {1 ,2 ,3 ,4 ,5};
y.Except(x).Any(); //true, not all items from y are in x
x.Except(y).Any(); // false, all items from x are in y
This isn't exactly what you asked for, but performance wise you should definitely use HashSet's IsProperSubsetOf. It can do what you want in orders of magnitude less time:
HashSet<string> a = new HashSet<string>(list1.Select(x => x.itemname));
HashSet<string> b = new HashSet<string>(list2.Select(x => x.itemname));
a.IsProperSubsetOf(b)
Explanation: HashSet uses the item's GetHashCode value and Equals method in an efficient way to compare items. That means that when it internally goes through the values in b it doesn't have to compare it to all other items in a. It uses the hash code (and an internal hash function) to check whether it already has that value or doesn't.
Because it does only a single check for every item (each check is O(1)) it's much faster than checking all items in a which would take O(n) (for each item in b that is).
B.All(itB=>myItems.Select(itA=>itA.ItemName).Contains(itB.ItemName))
Will run in O(N^2) time, but it's cool you can do that in just one rather unreadable line.
Here is another way. I included a way to include and exclude the list comparison.
var a = new List<int> { 1, 2, 3, 4, 5 };
var b = new List<int> { 1, 2, 3 };
//Exists in list a but not in b
var c = (from i
in a
let found = b.Any(j => j == i)
where !found select i)
.ToList();
//Exists in both lists
var d = (from i
in a
let found = b.Any(j => j == i)
where found select i)
.ToList();
I have around 200K records in a list and I'm looping through them and forming another collection. This works fine on my local 64 bit Win 7 but when I move it to a Windows Server 2008 R2, it takes a lot of time. There is difference of about an hour almost!
I tried looking at Compiled Queries and am still figuring it out.
For various reasons, we cant do a database join and retrieve the child values
Here is the code:
//listOfDetails is another collection
List<SomeDetails> myDetails = null;
foreach (CustomerDetails myItem in customerDetails)
{
var myList = from ss in listOfDetails
where ss.CustomerNumber == myItem.CustomerNum
&& ss.ID == myItem.ID
select ss;
myDetails = (List<SomeDetails>)(myList.ToList());
myItem.SomeDetails = myDetails;
}
I would do this differently:
var lookup = listOfDetails.ToLookup(x => new { x.CustomerNumber, x.ID });
foreach(var item in customerDetails)
{
var key = new { CustomerNumber = item.CustomerNum, item.ID };
item.SomeDetails = lookup[key].ToList();
}
The big benefit of this code is that it only has to loop through the listOfDetails once to build the lookup - which is nothing more than a hash map. After that we just get the values using the key, which is very fast as that is what hash maps are built for.
I don't know why you have the difference in performance, but you should be able to make that code perform better.
//listOfDetails is another collection
List<SomeDetails> myDetails = ...;
detailsGrouped = myDetails.ToLookup(x => new { x.CustomerNumber, x.ID });
foreach (CustomerDetails myItem in customerDetails)
{
var myList = detailsGrouped[new { CustomerNumber = myItem.CustomerNum, myItem.ID }];
myItem.SomeDetails = myList.ToList();
}
The idea here is to avoid the repeated looping on myDetails, and build a hash based lookup instead. Once that is built, it is very cheap to do a lookup.
The inner ToList() is forcing an evaluation on each loop, which has got to hurt. The SelectMany might let you avoid the ToList, something like this :
var details = customerDetails.Select( item => listOfDetails
.Where( detail => detail.CustomerNumber == item.CustomerNum)
.Where( detail => detail.ID == item.ID)
.SelectMany( i => i as SomeDetails )
);
If you first get all the SomeDetails and then assign them to the items, it might speed up. Or it might not. You should really profile to see where the time is being taken.
I think you'd probably benefit from a join here, so:
var mods = customerDetails
.Join(
listOfDetails,
x => Tuple.Create(x.ID, x.CustomerNum),
x => Tuple.Create(x.ID, x.CustomerNumber),
(a, b) => new {custDet = a, listDet = b})
.GroupBy(x => x.custDet)
.Select(g => new{custDet = g.Key,items = g.Select(x => x.listDet).ToList()});
foreach(var mod in mods)
{
mod.custDet.SomeDetails = mod.items;
}
I didn't compile this code...
With a join the matching of items from one list against another is done by building a hashtable-like collection (Lookup) of the second list in O(n) time. Then it's a matter of iterating the first list and pulling items from the Lookup. As pulling data from a hashtable is O(1), the iterate/match phase also only takes O(n), as does the subsequent GroupBy. So in all the operation should take ~O(3n) which is equivalent to O(n), where n is the length of the longer list.
I have a list stored in resultlist as follows:
var resultlist = results.ToList();
It looks something like this:
ID FirstName LastName
-- --------- --------
1 Bill Smith
2 John Wilson
3 Doug Berg
How do I remove ID 2 from the list?
List<T> has two methods you can use.
RemoveAt(int index) can be used if you know the index of the item. For example:
resultlist.RemoveAt(1);
Or you can use Remove(T item):
var itemToRemove = resultlist.Single(r => r.Id == 2);
resultList.Remove(itemToRemove);
When you are not sure the item really exists you can use SingleOrDefault. SingleOrDefault will return null if there is no item (Single will throw an exception when it can't find the item). Both will throw when there is a duplicate value (two items with the same id).
var itemToRemove = resultlist.SingleOrDefault(r => r.Id == 2);
if (itemToRemove != null)
resultList.Remove(itemToRemove);
Short answer:
Remove (from list results)
results.RemoveAll(r => r.ID == 2); will remove the item with ID 2 in results (in place).
Filter (without removing from original list results):
var filtered = result.Where(f => f.ID != 2); returns all items except the one with ID 2
Detailed answer:
I think .RemoveAll() is very flexible, because you can have a list of item IDs which you want to remove - please regard the following example.
If you have:
class myClass {
public int ID; public string FirstName; public string LastName;
}
and assigned some values to results as follows (used for all examples below):
var results = new List<myClass> {
new myClass { ID=1, FirstName="Bill", LastName="Smith" }, // results[0]
new myClass { ID=2, FirstName="John", LastName="Wilson" }, // results[1]
new myClass { ID=3, FirstName="Doug", LastName="Berg" }, // results[2]
new myClass { ID=4, FirstName="Bill", LastName="Wilson" } // results[3]
};
Then you can define a list of IDs to remove:
var removeList = new List<int>() { 2, 3 };
And simply use this to remove them:
results.RemoveAll(r => removeList.Any(a => a==r.ID));
It will remove the items 2 and 3 and keep the items 1 and 4 - as specified by the removeList. Note that this happens in place, so there is no additional assigment required.
Of course, you can also use it on single items like:
results.RemoveAll(r => r.ID==4);
where it will remove Bill with ID 4 in our example.
A last thing to mention is that lists have an indexer, that is, they can also be accessed like a dynamic array, i.e. results[3] will give you the 4th element in the results list (because the first element has the index 0, the 2nd has index 1 etc).
So if you want to remove all entries where the first name is the same as in the 4th element of the results list, you can simply do it this way:
results.RemoveAll(r => results[3].FirstName == r.FirstName);
Note that afterwards, only John and Doug will remain in the list, Bill is removed (the first and last element in the example). Important is that the list will shrink automatically, so it has only 2 elements left - and hence the largest allowed index after executing RemoveAll in this example is 1 (which is results.Count() - 1).
Some Trivia:You can use this knowledge and create a local function
void myRemove() { var last = results.Count() - 1;
results.RemoveAll(r => results[last].FirstName == r.FirstName); }
What do you think will happen, if you call this function twice?
Like
myRemove(); myRemove();
Answer (click to show):
The first call will remove Bill at the first and last position, the second call will remove Doug and only John Wilson remains in the list.
Note: Since C# Version 8, you can as well write results[^1] instead of var last = results.Count() - 1; and results[last]:
void myRemove() => results.RemoveAll(r => results[^1].FirstName == r.FirstName);
So you would not need the local variable last anymore (see indices and ranges). Furthermore, since it is a one-liner, you don't require the curly braces and can use => instead.
For a list of all the new features in C#, look here.
DotNetFiddle: Run the demo
resultList = results.Where(x=>x.Id != 2).ToList();
There's a little Linq helper I like that's easy to implement and can make queries with "where not" conditions a little easier to read:
public static IEnumerable<T> ExceptWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
return source.Where(x=>!predicate(x));
}
//usage in above situation
resultList = results.ExceptWhere(x=>x.Id == 2).ToList();
You don't specify what kind of list, but the generic List can use either the RemoveAt(index) method, or the Remove(obj) method:
// Remove(obj)
var item = resultList.Single(x => x.Id == 2);
resultList.Remove(item);
// RemoveAt(index)
resultList.RemoveAt(1);
More simplified:
resultList.Remove(resultList.Single(x => x.Id == 2));
there is no needing to create a new var object.
There is another approach. It uses List.FindIndex and List.RemoveAt.
While I would probably use the solution presented by KeithS (just the simple Where/ToList) this approach differs in that it mutates the original list object. This can be a good (or a bad) "feature" depending upon expectations.
In any case, the FindIndex (coupled with a guard) ensures the RemoveAt will be correct if there are gaps in the IDs or the ordering is wrong, etc, and using RemoveAt (vs Remove) avoids a second O(n) search through the list.
Here is a LINQPad snippet:
var list = new List<int> { 1, 3, 2 };
var index = list.FindIndex(i => i == 2); // like Where/Single
if (index >= 0) { // ensure item found
list.RemoveAt(index);
}
list.Dump(); // results -> 1, 3
Happy coding.
Try this code:
resultlist.Remove(resultlist.Find(x => x.ID == 2));
... or just resultlist.RemoveAt(1) if you know exactly the index.
{
class Program
{
public static List<Product> list;
static void Main(string[] args)
{
list = new List<Product>() { new Product() { ProductId=1, Name="Nike 12N0",Brand="Nike",Price=12000,Quantity=50},
new Product() { ProductId =2, Name = "Puma 560K", Brand = "Puma", Price = 120000, Quantity = 55 },
new Product() { ProductId=3, Name="WoodLand V2",Brand="WoodLand",Price=21020,Quantity=25},
new Product() { ProductId=4, Name="Adidas S52",Brand="Adidas",Price=20000,Quantity=35},
new Product() { ProductId=5, Name="Rebook SPEED2O",Brand="Rebook",Price=1200,Quantity=15}};
Console.WriteLine("Enter ProductID to remove");
int uno = Convert.ToInt32(Console.ReadLine());
var itemToRemove = list.Find(r => r.ProductId == uno);
if (itemToRemove != null)
list.Remove(itemToRemove);
Console.WriteLine($"{itemToRemove.ProductId}{itemToRemove.Name}{itemToRemove.Brand}{itemToRemove.Price}{ itemToRemove.Quantity}");
Console.WriteLine("------------sucessfully Removed---------------");
var query2 = from x in list select x;
foreach (var item in query2)
{
/*Console.WriteLine(item.ProductId+" "+item.Name+" "+item.Brand+" "+item.Price+" "+item.Quantity );*/
Console.WriteLine($"{item.ProductId}{item.Name}{item.Brand}{item.Price}{ item.Quantity}");
}
}
}
}