使用for循环合并列表

时间:2016-12-15 08:38:22

标签: c# list merge

我正在开发一种算法,可以生成两种类型的推荐,餐馆和菜肴。所有这一切都很好,但我想在一个列表中合并这两种类型的建议,这是我遇到一些问题的地方。从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()表示其余对象吗?我不知道如何检查哪个列表超出范围。

请告诉我是否应该提前详细说明并提前致谢!

编辑: - 已删除,因为它不公平.-

6 个答案:

答案 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 methodMoreLinq 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;
                    }
            }
        }
    }