Linq запрос, чтобы вернуть самый дешевый продукт, только если он уникален - PullRequest
2 голосов
/ 22 января 2020

У меня есть список Product с Price. Я хотел бы получить самый дешевый один, только если он уникальный . Если имеется более чем одного Product с одинаковой самой низкой ценой, он не должен возвращать .

В приведенном ниже примере для uniqProductList запрос должен возвращать BestOne, в то время как для dupProductList продукт возвращаться не должен.

Как мне написать запрос Linq ?

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public DateTime ExpiryDate { get; set; }
}

List<Product> uniqProductList = new List<Product>() {
    new Product { Name = "GoodOne", Price = 12M },
    new Product { Name = "NiceOne", Price = 12M },
    new Product { Name = "ExpensiveOne", Price = 15M },
    new Product { Name = "BestOne", Price = 9.99M }
};

List<Product> dupProductList = new List<Product>() {
    new Product { Name = "GoodOne", Price = 12M },
    new Product { Name = "NiceOne", Price = 12M },
    new Product { Name = "ExpensiveOne", Price = 15M },
};

Ответы [ 7 ]

3 голосов
/ 22 января 2020

Это один из способов, если вы хотите сделать это в одном запросе:

Product result = uniqProductList
    .GroupBy(x => x.Price)
    .OrderBy(x => x.Key)
    .Take(1)
    .FirstOrDefault(x => x.Count() == 1)?
    .FirstOrDefault();
  • Сгруппировать результаты по цене
  • Упорядочить по цене, чтобы самый дешевый был первый результат
  • Возьмите первый результат, так как остальные нас не интересуют
  • Возврат группировки, если в группе есть только один результат
  • Возвращение значения

Почти наверняка есть какой-то другой способ, более быстрый.

2 голосов
/ 22 января 2020

Вы ищете ArgMax, который не входит в стандарт Linq , но может быть реализован вручную с помощью Aggregate. Имея коллекцию самых дешевых Product s, мы можем вернуть null, если у нас их более 1:

  using System.Linq;

  ...

  List<Product> source = ...

  var bests = source
    .Aggregate(new List<Product>(), (s, a) => {
      if (s.Count <= 0 || s[0].Price == a.Price)
        s.Add(a);
      else if (a.Price <= s[0].Price) {
        s.Clear();
        s.Add(a);
      }

      return s;
    });

  Product best = bests.Count == 1 ? bests[1] : default(Product);
1 голос
/ 22 января 2020

Вы можете сгруппировать элементы по их цене и получить самую дешевую группу:

var cheapestGrp = uniqProductList.GroupBy(i => i.Price).OrderBy(i => i.Key).First();

Затем, основываясь на количестве элементов группы, вернуть единственный элемент или ничего не возвращать:

if (cheapestGrp.Count() > 1)
    return null;
else
    return cheapestGrp.ToList().First();
0 голосов
/ 22 января 2020

Это еще одно решение:

 var product = uniqProductList.OrderBy(a => a.Price)
          .GroupBy(a => a.Price).FirstOrDefault()
          .Aggregate(new List<Product>(), (result, item) =>
          {
            result.Add(item);
            if (result.Count() > 1)
                result = new List<Product>();
            return result;
        }).FirstOrDefault();

Сначала вы можете получить самую низкую цену, а затем сгруппировать ее.

0 голосов
/ 22 января 2020

Вы можете использовать GroupBy, а затем использовать Where, чтобы получить элементы, где есть только один Count, а затем просто отсортировать в порядке возрастания:

var result = uniqProductList
    .GroupBy(u => u.Price)
    .Select(grp => new { grp.Key, Count = grp.Count(), Items = grp.ToList() })
    .Where(s => s.Count == 1)
    .OrderBy(o=> o.Key)
    .FirstOrDefault();

Пример:

List<Product> uniqProductList = new List<Product>() {
            new Product { Name = "GoodOne", Price = 12M },
            new Product { Name = "NiceOne", Price = 12M },
            new Product { Name = "ExpensiveOne", Price = 15M },
            new Product { Name = "BestOne", Price = 9.99M }
};

List<Product> dupProductList = new List<Product>() {
            new Product { Name = "GoodOne", Price = 12M },
            new Product { Name = "NiceOne", Price = 12M },
            new Product { Name = "ExpensiveOne", Price = 15M },
};

var result = uniqProductList
    .GroupBy(u => u.Price)
    .Select(grp => new { grp.Key, Count = grp.Count(), Items = grp.ToList() })
    .Where(s => s.Count == 1)
    .OrderBy(o=> o.Key)
    .FirstOrDefault();
0 голосов
/ 22 января 2020
result = Products.GroupBy(x => x.Price)
                  .Select(g => new { g.Name, Count = g.Count() 
                          })
                  .Orderby(s.Count)
                  .Select(x.Name, x.Count).FirstOrDefault();    

if(result.Count == 1){
  return result;
}
else{
  return null;
}
0 голосов
/ 22 января 2020

Я бы предложил это решение:

public Product? TryGetBestOne(IEnumerable<Product> products)
{
    var bestProducts = products
        .GroupBy(x => x.Price)
        .OrderBy(x => x.Key)
        .FirstOrDefault()?
        .ToArray() ?? Array.Empty<Product>();

    return bestProducts.Count() == 1 ? bestProducts.Single() : null;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...