我正在开发一种算法,可以生成两种类型的推荐,餐馆和菜肴。所有这一切都很好,但我想在一个列表中合并这两种类型的建议,这是我遇到一些问题的地方。从my previous question我得出结论,我需要一个包装类,我已经设置了这样的类:
public class RecommenderItem
{
public Guid Id { get; set; }
public object Entity { get; set; }
}
现在我想要替换两种类型的推荐,所以列表看起来像这样:
[Restaurant][Dish][Restaurant][Dish][Restaurant][Dish] //Etc...
请注意,这些建议完全是分开的。它们纯粹基于用户的偏好生成,并且它们之间没有相关性。我的产品所有者希望在我们的应用主页上显示这些建议。
这些列表的长度不同,所以如果我添加了列表中的所有项目,我只想添加其他列表中的其余对象。可能的情况可能如下所示:
/*Other objects before this...*/[Dish][Restaurant][Dish][Dish][Dish] //Etc...
这里餐馆对象列表用完了,我只是想在列表末尾添加剩余的菜肴推荐。
我已经做到了这一点,但我不确定如何捕获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;
}
有谁知道我该怎么做?我可以抓住IndexOutOfBounds
例外并使用.AddRange()
表示其余对象吗?我不知道如何检查哪个列表超出范围。
请告诉我是否应该提前详细说明并提前致谢!
编辑: - 已删除,因为它不公平.-
答案 0 :(得分: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;
}
}
}
如果您正在使用包含the ZipLongest
extension method的MoreLinq
NuGet包,则可以使用以下简化实现:
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 };
}
}
<强>附录强>
在@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;
}
}
}
}
有了这些,我意识到你不需要写一个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();
答案 1 :(得分:8)
我的建议是简单地implicit operator:
public static implicit operator RecommenderItem(Restaurant restaurant) {
return new RecommenderItem { Id = restaurant.Id, Entity = restaurant };
}
然后您可以轻松转换这些类型:
Restaurant rest = //...
RecommenderItem rItem = rest; // here the implicit operator is called
执行此操作后,您只需使用一个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]);
}
这将使您的工作更轻松。
答案 2 :(得分:6)
嗯,可能有更优雅的LINQ解决方案,但你已经拥有最多,它也是一种非常有效的方法:
tifffile.save(ImagePath, ImageData)
答案 3 :(得分:5)
一种简单的方法是:
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;
}
}
}
答案 4 :(得分:2)
创建了两个相同类型RecommenderItem
的餐厅和菜肴阵列后,您可以使用Zip方法:
var restaurants = restaurantRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var dishes = dishRecommendations.Select(x => new RecommenderItem {
Id = x.Id,
Entity = x
}).ToArray();
var output = restaurants.Zip(dishes, (r, d) => new[] { r, d })
.SelectMany(r => r).Concat(dishes.Skip(restaurants.Length))
.Concat(restaurants.Skip(dishes.Length));
答案 5 :(得分:0)
Restaraunt和Dish必须共享基本类型:
restaurantRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
dishRecommendations.Select(item => new RecommenderItem()
{
Id = item.Id,
Entity = item
});
一旦出现这种情况,您可以使用类似Zip
的略微修改版本(来自System.Linq
):
private static IEnumerable<T> ZipThrough<T>(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
using (var e1 = first.GetEnumerator())
{
using (var e2 = second.GetEnumerator())
{
while (true)
if (e1.MoveNext())
{
yield return e1.Current;
if (e2.MoveNext()) yield return e2.Current;
}
else if (e2.MoveNext())
{
yield return e2.Current;
}
else
{
break;
}
}
}
}