使用for循环合并列表

Ran*_*ger 8 c# merge list

我正在研究一种可以生成两种推荐,餐馆和菜肴的算法.所有这一切都很好,但我想在一个列表中合并这两种类型的建议,这是我遇到一些问题的地方.从我之前的问题我得出结论,我需要一个包装类,我已经设置了这样:

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)


Mat*_*usz 8

我的建议是只做简单的隐式运算符:

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)

这将使您的工作更轻松.

  • 好吧,你还需要一个从'Dish`到`RecommenderItem`.就个人而言,我不喜欢这种神奇的转换,因为它们对所有不熟悉课程的人都是隐藏的.但无论如何,好主意. (3认同)

Tim*_*ter 6

嗯,可能有更优雅的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)


InB*_*een 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)