Is it possible to ignore a list item using the Skip method of the LINQ then apply skip on the list without losing that item?

Viewed 1608

I want to implement paging on a list of data. The list has some fake items in itself as flag items for doing some specific work on the data. A simplified version of what I have done is as below:

List<Model> list = _myServiceContract.MyServiceMethod(MySearchModel);
pagedData = list.Skip((page - 1) * pageSize).Take(pageSize);

But my problem with this way is that the fake items will be counted in the Skip and Take methods of the Linq.

I want to know if it is possible to ignore those fake items in the Skip method then apply Skip on the list, including fake items by some changes in the Take method for example or something similar.

Edit: The first list is ordered before doing paging and those fake items are in ordered places also. You should know the order of the list is important for me.

3 Answers

To be honest I don't like what you're doing. I'd consider modelling your data better in future. However, here's a couple of extension methods that'll get the job done...

(I've essentially modified the original implementations of Skip and Take from https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs)

public static IEnumerable<TSource> SkipAndInclude<TSource>(this IEnumerable<TSource> source, int count, Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (predicate == null) throw new ArgumentNullException(nameof(predicate));

    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        while (count > 0 && e.MoveNext())
        {
            if (!predicate(e.Current)) count--;
        }
        if (count <= 0)
        {
            while (e.MoveNext()) yield return e.Current;
        }
    }
}

public static IEnumerable<TSource> TakeAndInclude<TSource>(this IEnumerable<TSource> source, int count, Func<TSource, bool> predicate)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (predicate == null) throw new ArgumentNullException(nameof(predicate));

    if (count > 0)
    {
        foreach (TSource element in source)
        {
            yield return element;
            if (!predicate(element)) count--;
            if (count == 0) break;
        }
    }
}

Usage...

pagedData = list.SkipAndInclude((page - 1) * pageSize, x => x.Fake).TakeAndInclude(pageSize, x => x.Fake);

How about iterating over the list manually to find the index of fake items, then adding them in later. Something like this:

var fakeItems = new Dictionary<int, Model>();
var unpaged = MyService.MyServiceMethod(MySearchModel);

for (var i = 0; i < unpaged.Count; i++)
{
    if (unpaged[i].IsFake)
        fakeItems.Add(i, unpaged[i]);
}

var paged = unpaged.Where(x => !x.IsFake)
    .Skip((page - 1) * pageSize)
    .Take(pageSize);

foreach (var item in fakeItems){
{
    if((pageSize * (page - 1)) <= item.Key && item.Key < (pageSize * page))
        paged.Insert(item.Key, item.Value);
}

Just use a where to ignore those items like this:

List<Model> list = MyService.MyServiceMethod(MySearchModel);
pagedData = list.Where(x => !x.Fake).Skip((page - 1) * pageSize).Take(pageSize).ToList();

Do not forget to Order your items. Otherwise you could have problems when paging (skip & take).

Related