Search code examples
c#linqunity-game-engineinventory

How can I convert this linq to bool


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.


Solution

  • "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(...)