在 C# 列表中将项目插入到正确的位置。

3

我有一个包含产品的列表 (List<Product>),每个产品都有一个id、名称和价格。

如果我想要按价格降序排序该列表,是否有一行代码或扩展方法可以让我将新产品插入到正确的位置?

public class Product
{
   public int Id {get; set;}
   public string Name {get; set;}
   public int Price {get; set;}   // assume only whole integers for price
}

public class Main()
{
   List<Product> products = new();
   products.Add(new Product(id= 1, Name="Product1", Price=10 };
   products.Add(new Product(id= 2, Name="Product2", Price=15 };
   products.Add(new Product(id= 3, Name="Product3", Price=11 };
   products.Add(new Product(id= 4, Name="Product4", Price=20 }; 


   products = products.OrderByDescending(prd => prd.Price).ToList();

  var newProduct = new({id = 5, Name="new product", Price = 17})

   // Is there an short solution available that allows me to insert a new product with
   // price = 17 and that will be inserted between products with price 15 and 20?
   // Without repeatedly iterating over the list to find the one lower and the one higher 
   // than the new price and recalculate the index...
   var lastIndex = products.FindLastIndex(x => x.Price >= newProduct.Price);
   products.Insert(lastIndex + 1, p5);     
}

编辑解决方案:我赞同 Tim Schmelter 的答案是最正确的。它不是单行代码,需要一个自定义扩展方法,但我认为没有单行解决方案可用。添加它并执行 OrderByDescending() 即可简单解决,但接下来的代码将依赖于 OrderByDescending() 语句...


“正确的列表位置”是什么意思? - Roman Ryzhiy
2
只需在 .Add 后调用 products = products.OrderByDescending(prd => prd.Price).ToList(); 或者使用 SortedList - Roman Ryzhiy
如果排序仅用于UI,我不会在模型中执行此操作,而是在需要的地方执行(例如,如果您正在使用WPF,则使用CollectionViewSource)。 - Klaus Gütter
1
公共类 Main - Tim Schmelter
1
这个回答解决了你的问题吗?如何按顺序将项目插入列表中? - Orace
2个回答

6

您可以使用 SortedList<TKey, TValue>

SortedList<int, Product> productList = new();
var p = new Product{ Id = 1, Name = "Product1", Price = 10 };
productList.Add(p.Price, p);
p = new Product { Id = 2, Name = "Product2", Price = 15 };
productList.Add(p.Price, p);
p = new Product { Id = 3, Name = "Product3", Price = 11 };
productList.Add(p.Price, p);
p = new Product { Id = 4, Name = "Product4", Price = 20 };
productList.Add(p.Price, p);

p = new Product { Id = 5, Name = "Product5", Price = 17 };
productList.Add(p.Price, p);

foreach(var x in productList) 
    Console.WriteLine($"{x.Key} {x.Value.Name}");

输出:

10 Product1
11 Product3
15 Product2
17 Product5
20 Product4

编辑说明:它不允许重复的键,就像字典一样。您可以通过使用 SortedList<int, List<Product>> 来解决这个问题。例如,使用此扩展方法:

public static class CollectionExtensions
{
    public static void AddItem<TKey, TValue>(this SortedList<TKey, List<TValue>> sortedList, TValue item, Func<TValue, TKey> getKey)
    {
        TKey key = getKey(item);
        if (sortedList.TryGetValue(key, out var list))
        {
            list.Add(item);
        }
        else
        {
            sortedList.Add(key, new List<TValue> { item });
        }
    }
}

用法:

SortedList<int, List<Product>> productLists = new();
productLists.AddItem(new Product { Id = 1, Name = "Product1", Price = 10 }, p => p.Price);
productLists.AddItem(new Product { Id = 2, Name = "Product2", Price = 10 }, p => p.Price);
productLists.AddItem(new Product { Id = 3, Name = "Product3", Price = 20 }, p => p.Price);
productLists.AddItem(new Product { Id = 4, Name = "Product4", Price = 20 }, p => p.Price);
productLists.AddItem(new Product { Id = 5, Name = "Product5", Price = 15 }, p => p.Price);

foreach (var x in productLists) 
    Console.WriteLine($"{x.Key} {string.Join("|", x.Value.Select(p => p.Name))}");

输出:

10 Product1|Product2
15 Product5
20 Product3|Product4

1

在将新元素添加到列表之前,您可以计算其位置,然后使用List.Insert方法。


如果性能很重要,并且列表随时保证已排序,List.BinarySearch 将非常有用。 - Klaus Gütter
二分查找适用于项目,而 OP 需要按项目属性进行排序。 - Orace
1
@Orace,有一个重载可以接受自定义比较器,因此这不是限制。 - Klaus Gütter
我认为这是代码示例的最后两行中所做的事情? - CMorgan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接