如何根据特定值从对象列表中获取对象? C#

时间:2021-05-14 20:51:51

标签: c#

如何根据价格从对象列表中获取对象?在下面的示例中,我有一个药店列表,我试图在其中比较药店之间的价格并选择价格最优惠的一家。

即对于“NyQuil”,CVS 给了我更低的价格,所以我想选择它。我怎样才能做到这一点? 我对 LINQfor..loop

没问题

代码

    public InventoryItem(string drug, decimal cost) {
      this.Drug = drug;
      this.Cost = cost;
    }

    public class Pharmacy {
        public string Name {get; set; }
        public List<InventoryItem> Inventory {get; set; }
        public decimal? EstimatedOrderItemCost { get; set; }
    
        public Pharmacy(string name, List<InventoryItem> inventories, decimal? estimatedOrderItemCost = null) {
            this.Name = name;
            this.Inventory = inventories;
            this.EstimatedOrderItemCost = estimatedOrderItemCost;
        }
    }

var pharmacyList = new List<Pharmacy> {
            cvsPharmacy, walgreensPharmacy
        };

// Option-1;:
// This gives me cost and drug name only, index[2] results from screen shot
 var test = pharmacyList.Select(x => x.Inventory.Where(y => y.Drug.ToLower() == item.Drug.ToLower()));

// Option-2:
// This gives me ""At least one object must implement IComparable." message but no error.
var test = pharmacyList.Select(x => x.Inventory.Min()).Where(y => y.Drug.ToLower() == item.Drug.ToLower());

输入

enter image description here

1 个答案:

答案 0 :(得分:0)

<块引用>

如何根据价格从对象列表中获取对象?

使用 LINQ 我们可以使用 .Where() 方法构造一个 IEnumerable 对象,该对象通过迭代集合的每个元素并在每个元素上运行一些提供的函数来填充,如果函数返回 true 然后将元素添加到结果中,如果它是 false 则被忽略。

有了它,我们可以“搜索”(如果您愿意的话)一个列表。

让我们找到包含我们正在寻找的药物的所有药房

// define an easier to read function that will tell is if an inventory has a drug
bool ContainsDrug(string DrugName, IEnumerable<InventoryItem> Inventory)
{
    foreach(var item in Inventory)
    {
        if(item.ToUpper().Equals(DrugName))
        {
             return true;
        }
    }
    return false;
}

// get a enumerable that tells us which pharmacies have the drug
var pharmaciesContainingNyQuil = pharmacyList.Where(pharmacy => ContainsDrug("NyQuil", pharmacy.Inventory));

既然我们拥有所有包含该药物的药房,我们应该按照该药物的价格对它们进行排序。

为此,我们需要使用 2 个不同的 LINQ 命令。

.Select() 命令遍历可枚举项,并根据您从提供给方法的函数返回的任何内容创建一个具有 NEW 类型的 NEW 可枚举项。

例如要将 int[] 转换为 string[],我们可以使用 select 命令。

它将遍历 int[] 并对每个元素运行函数 string UnamedLamda(int num) return num.ToString(); 并构建仅包含字符串的“新可枚举”。

var strings = new int[]{1,2,3,4}.Select( num => num.ToString());

// outputs
strings = {"1","2","3","4"}

我们可以使用它来将所有药房转换为一个更容易排序的可枚举对象。

要排序,我们可以使用 .Sort() System.Collections.Generic 命令。其实很简单。我们为 .Sort() 提供了一个 lambda expression 就像 .Where().Select() 但这次我们给它左右两个参数(它们不必那样命名) . System.Collections.Generic 然后遍历可枚举对象并将每个左侧与每个右侧进行比较,然后以这种方式对它们进行排序。

例如,如果我们想按字符串的长度对字符串列表进行排序,我们可以使用:

List<string> strs = {"1","two", "three", "four"};

// sort the list
strs.Sort((left,right) => left.Length.CompareTo(right.Length));

// this returns
{"1","two","four", "three"}

如果我们使用这两个命令,我们可以创建一个非常容易排序的新枚举

decimal FindPrice(string Drug, IEnumerable<InventoryItem> Inventory)
{
    foreach(var item in Inventory)
    {
         if(item.ToUpper().Equals(Drug))
         {
             return item.Cost;
         }
    }
}

var pharmaciesInEasyToSortTuples = pharmaciesContainingNyQuil.Select(pharmacy => (pharmacy, FindPrice("NyQuil",pharmacy.Inventory)));

// sort the pharmacies

var sortedPharmacies = pharmaciesInEasyToSortTuples.ToArray().Sort((left,right)=>left.item2.CompareTo(right.item2));

// return the lowest price
return sortedPharmacies?.FirstOrDefault().pharmacy ?? default;

现在如果我们把它们放在一起,我们可以得到一个最终的方法,它可以做你想要它做的事情,它可能看起来像这样:

Pharmacy FindDrugAtPharmacyWithLowestPrice(string Drug, IEnumerable<Pharmacy> Pharmacies)
{
    // we should check every pharmacy
    // we should check if the pharmacy has the drug
    // after we have all the pharmacies that contain the drug, sort them by price and grab the lowest one,

    // get the pharmacies that have the drug
    string name = Drug.ToUpper();


    // Pharmacies.Where -> looks through the pharmacies
    // pharmacy => pharmacy.Inventory.Select(x => x.Drug.ToUpper()) -> creates a List<string>(not really) that we can check to see if it contains the drug
    var pharmaciesContainingTheDrug =
        // search the pharmacies
        Pharmacies.Where(
            pharmacy =>
                // convert the inventory to a list of strings
                pharmacy.Inventory.Select(x => x.Drug.ToUpper())
                    // check the inventory for the drug
                    .Contains(name));

    if (pharmaciesContainingTheDrug?.Count() is null or 0)
    {
        // no pharmacies containing drug
        return default;
    }

    // now we have a enumerable that has all the pharmacies that contain the drug
    // sort by the lowest price
    // first convert to a more usable format so we can sort

    decimal GetPriceInList(string Drug, IEnumerable<InventoryItem> Inventory)
    {
        return Inventory.Where(x => x.Drug.ToUpper() == Drug)?.FirstOrDefault()?.Cost ?? default;
    }

    var sortedPharmacies = pharmaciesContainingTheDrug.Select(pharmacy => (pharmacy, GetPriceInList(name, pharmacy.Inventory)));

    // don't bother continuing if for some reason we couldn't construct the enumerable tuples
    if (sortedPharmacies?.Count() is null or 0)
    {
        return default;
    }

    // actually sort the pharmacies
    sortedPharmacies.ToList().Sort((left, right) => left.Item2.CompareTo(right.Item2));

    return sortedPharmacies?.FirstOrDefault().pharmacy ?? default;
}