选择前N个元素并记住出现的顺序

时间:2017-03-28 14:54:03

标签: c#

我需要从大量产品中选择相关产品的顶级N元素。 到目前为止,我有下面的代码,它的工作完美。

class Product
{
    public string Name;
    public double Rating;
    public List<Product> RelatedProducts;

    public List<Product> GetTopRelatedProducts(int N)
    {
        var relatedSet = new HashSet<Product>();
        var relatedListQueue = new Queue<List<Product>>();
        if (RelatedProducts != null && RelatedProducts.Count > 0)
            relatedListQueue.Enqueue(RelatedProducts);
        while (relatedListQueue.Count > 0)
        {
            var relatedList = relatedListQueue.Dequeue();
            foreach (var product in relatedList)
            {
                if (product != this && relatedSet.Add(product) && product.RelatedProducts != null && product.RelatedProducts.Count > 0)
                    relatedListQueue.Enqueue(product.RelatedProducts);
            }
        }
        return relatedSet.OrderByDescending(x => x.Rating).Take(N).OrderBy(/*How to order by occurrence here? */).ToList();
    }
}

现在,我希望GetTopRelatedProducts方法记住热门N产品的发生顺序。首先添加到HashSet的产品将在返回列表的开头。

例如,如果我有这种情况:

//...
relatedSet.Add(new Product(){Name="A", Rating=3});
relatedSet.Add(new Product(){Name="B", Rating=4});
relatedSet.Add(new Product(){Name="C", Rating=5});
//...

如果N = 2,则该方法应返回:B,C而不是C,B,因为B首先添加到HashSet

所以我将方法中的return语句改为:

        var relatedSetCopy = relatedSet.ToList();
        return (from p in relatedSet.OrderByDescending(x => x.Rate).Take(N)
                    join c in relatedSetCopy on p.Name equals c.Name
                    let index = relatedSetCopy.IndexOf(c)
                    orderby index
                    select p).ToList();

基本上,我使用LINQ Join按照Rating上的排序方式重新排序列表。

我想这样做是因为首次添加的产品与所选产品的相似性高于其他产品。

我在这里有两个问题:

  1. 是否有更好的方法来重新订购返回的列表?
  2. 是否有更好的设计来处理产品之间的关系? (我正在考虑实现树结构。因此对象导航和检索会更快)

1 个答案:

答案 0 :(得分:3)

  

有没有更好的方法来重新订购返回的列表?

您可以简单地Intersect relatedSet与前N个相关的重新排序集,因为Intersect会根据第一个序列中的顺序生成项目。

所以而不是

return relatedSet.OrderByDescending(x => x.Rating).Take(N).ToList();

你会用

return relatedSet.Intersect(relatedSet.OrderByDescending(x => x.Rating).Take(N)).ToList();