我正在研究一种可以生成两种推荐,餐馆和菜肴的算法.所有这一切都很好,但我想在一个列表中合并这两种类型的建议,这是我遇到一些问题的地方.从我之前的问题我得出结论,我需要一个包装类,我已经设置了这样:
public class RecommenderItem
{
public Guid Id { get; set; }
public object Entity { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
现在我想要替换两种类型的建议,所以列表看起来像这样:
[Restaurant][Dish][Restaurant][Dish][Restaurant][Dish] //Etc...
Run Code Online (Sandbox Code Playgroud)
请注意,这些建议完全是分开的.它们纯粹基于用户的偏好生成,并且它们之间没有相关性.我的产品所有者希望在我们的应用主页上显示这些建议.
这些列表的长度不同,所以如果我添加了列表中的所有项目,我只想添加其他列表中的其余对象.可能的情况可能如下所示:
/*Other objects before this...*/[Dish][Restaurant][Dish][Dish][Dish] //Etc...
Run Code Online (Sandbox Code Playgroud)
这里的餐馆对象列表用完了,我只是想在列表的末尾添加剩余的菜肴推荐.
我已经做到了这一点,但我不确定如何捕获IndexOutOfBounds异常并在最后添加剩余的剩余对象.
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations,
List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = 0;
//Check which list is longer and use that count
if (restaurantRecommendations.Count > dishRecommendations.Count)
count = dishRecommendations.Count;
else
count = restaurantRecommendations.Count;
for (int i = 0; i < count; i++)
{
//I'm fully aware this isn't the most optimal way of doing this,
//but I'm only looking at functionality here, optimizing performance comes later.
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
有谁知道我怎么能做到这一点?我可以捕获IndexOutOfBounds异常并.AddRange()用于剩余的对象吗?我不知道如何检查哪个列表超出范围.
如果我需要详细说明并提前感谢,请告诉我!
编辑: -删除因为不公平.-
Mat*_*son 15
这是一种相当简洁的方法.
虽然不是Linq,但它的工作原理是Linq的工作方式是推迟做任何工作,直到枚举结果序列为止:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
using (var r = restaurants.GetEnumerator())
using (var d = dishes.GetEnumerator())
{
while (true)
{
bool rAvailable = r.MoveNext();
bool dAvailable = d.MoveNext();
if (rAvailable)
yield return new RecommenderItem { Id = r.Current.Id, Entity = r.Current };
if (dAvailable)
yield return new RecommenderItem { Id = d.Current.Id, Entity = d.Current };
if (!rAvailable && !dAvailable)
break;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您正在使用MoreLinq包含扩展方法的NuGet包,则ZipLongest可以使用以下简化实现:
public static IEnumerable<RecommenderItem> Merge(IEnumerable<Restaurant> restaurants, IEnumerable<Dish> dishes)
{
foreach (var item in restaurants.ZipLongest(dishes, (r, d) => new { r, d }))
{
if (item.r != null)
yield return new RecommenderItem { Id = item.r.Id, Entity = item.r };
if (item.d != null)
yield return new RecommenderItem { Id = item.d.Id, Entity = item.d };
}
}
Run Code Online (Sandbox Code Playgroud)
附录
正如@InBetween在他的回答中所述,您可以将交错逻辑放入扩展方法中.这是我的版本; 它基本上是相同的,除了我添加了一个小优化,以避免.MoveNext()在没有必要时调用:
public static class EnumerableExt
{
public static IEnumerable<T> Interleave<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var ae = a.GetEnumerator())
using (var be = b.GetEnumerator())
{
bool aAvailable = true;
bool bAvailable = true;
while (aAvailable || bAvailable)
{
aAvailable = aAvailable && ae.MoveNext();
bAvailable = bAvailable && be.MoveNext();
if (aAvailable)
yield return ae.Current;
if (bAvailable)
yield return be.Current;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
一旦你有了,我意识到你不需要编写implict运算符.相反,您可以在调用之前将两个序列转换为结果类型Interleave():
var restaurantsAsRecommenderItems =
restaurantRecommendations
.Select(r => new RecommenderItem {Id = r.Id, Entity = r});
var dishesAsRecommenderItems =
dishRecommendations
.Select(d => new RecommenderItem {Id = d.Id, Entity = d});
var result =
restaurantsAsRecommenderItems
.Interleave(dishesAsRecommenderItems)
.ToList();
Run Code Online (Sandbox Code Playgroud)
我的建议是只做简单的隐式运算符:
public static implicit operator RecommenderItem(Restaurant restaurant) {
return new RecommenderItem { Id = restaurant.Id, Entity = restaurant };
}
Run Code Online (Sandbox Code Playgroud)
然后你有可能轻松转换这些类型:
Restaurant rest = //...
RecommenderItem rItem = rest; // here the implicit operator is called
Run Code Online (Sandbox Code Playgroud)
执行此操作后,您只需使用一个for循环:
int count = Math.Max(restaurantRecommendations.Count, dishRecommendations.Count);
for ( int i = 0; i < count; i++ ) {
if ( i < restRecommendations.Count )
output.Add(restRecommendations[i]);
if ( i < dishRecommendations.Count )
output.Add(dishRecommendations[i]);
}
Run Code Online (Sandbox Code Playgroud)
这将使您的工作更轻松.
嗯,可能有更优雅的LINQ解决方案,但你已经是最多的,它也是一种非常有效的方法:
public List<RecommenderItem> GetMergedRecommendationLists(List<Restaurant> restaurantRecommendations, List<Dish> dishRecommendations)
{
//Setting up the output list.
List<RecommenderItem> output = new List<RecommenderItem>();
int count = Math.Min(restaurantRecommendations.Count, dishRecommendations.Count);
for (int i = 0; i < count; i++)
{
var restRecommendation = restaurantRecommendations[i];
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
int remainingRestaurant = restaurantRecommendations.Count - count;
int remainingDishes = dishRecommendations.Count - count;
if (remainingRestaurant > 0)
{
for (int i = count; i < restaurantRecommendations.Count; i++)
{
var restRecommendation = restaurantRecommendations[i];
output.Add(new RecommenderItem()
{
Id = restRecommendation.Id,
Entity = restRecommendation
});
}
}
else if (remainingDishes > 0)
{
for (int i = count; i < dishRecommendations.Count; i++)
{
var dishRecommendation = dishRecommendations[i];
output.Add(new RecommenderItem()
{
Id = dishRecommendation.Id,
Entity = dishRecommendation
});
}
}
return output;
}
Run Code Online (Sandbox Code Playgroud)
一个简单的方法是:
public static IEnumerable<T> Merge<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while (firstEnumerator.MoveNext())
{
yield return firstEnumerator.Current;
if (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
while (secondEnumerator.MoveNext())
{
yield return secondEnumerator.Current;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1483 次 |
| 最近记录: |