-2

Here is another way Split a List into smaller lists of N size

The purpose of this post is to share knowledge involving "Linq" and opinions without using "for" ties and "ranges" directly.

Example: I have a list of 100 items and I need to make it into 10 lists.

I use the follow script, somebody has a better way or more performatic?

var subLists = myList.Select((x, i) => new { Index = i, Item = x })
                     .GroupBy(x => x.Index / "MAXIMUM ITEMS ON SUBLIST")
                     .Select(x => x.Select(v => X.Item).ToList());

3 Answers3

4

It`s a slow operation

(x, i) => new { Index = i, Item = x }

Here's an extension method that will work with any list

public static IEnumerable<List<T>> splitList<T>(List<T> items, int size)  
{        
    for (int i=0; i < items.Count; i+= size) 
    { 
        yield return items.GetRange(i, Math.Min(size, items.Count - i)); 
    }  
}

OR better performance

public static List<List<T>> splitList<T>(this List<T> items, int size)
{
    List<List<T>> list = new List<List<T>>();
    for (int i = 0; i < items.Count; i += size)
        list.Add(items.GetRange(i, Math.Min(size, items.Count - i)));
    return list;
}
Hocas
  • 96
  • 6
1

Let's create a generic answer. One that works for any sequence of any length, where you want to split the sequence into a sequence of sub-sequences, where every sub-sequence has a specified length, except maybe for the last:

For example:

IEnumerable<int> items = {10, 11, 12, 13, 14, 15, 16, 17};

// split into subsequences of length 3:
IEnumerable<IEnumerable> splitSequence = items.Split(3);
// splitSequence is a sequence of 3 subsequences:
// {10, 11, 12},
// {13, 14, 15},
// {16, 17}

We'll do this by creating an extension method. This way, the method Split can be used as any LINQ function. See extension methods demystified. To make it efficient, I'll enumerate only once, and I don't enumerate any more items than requested for.

IEnumerable<TSource> Split(this IEnumerable<TSource> source, int splitSize)
{
    // TODO: exception if null source, or non-positive splitSize

    // Get the enumerator and enumerate enough elements to return
    IEnumerator<TSource> enumerator = source.GetEnumerator();
    while (enumerator.MoveNext())
    {
        // there are still items in the source; fill a new sub-sequence
        var subSequence = new List<Tsource>(SplitSize);
        do
        {   // add the current item to the list:
            subSequence.Add(enumerator.Current);
        }
        // repeat this until the subSequence is full or until source has no more elements:
        while (subSequence.Count() < splitSize && enumerator.MoveNext());

        // return the subSequence
        yield return subSequence;
     }
}

Usage:

// Get all Students that live in New York, split them into groups of 10 Students
// and return groups that have at least one Law Student 
var newYorkLasStudentGroups = GetStudents();
   .OrderBy(student => student.UniversityLocation == "New York")
   .Split(10)
   .Where(studentGroup => studentGroup.Any(student => student.Study == "Law"));
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
0

This question is not a duplicate. As mentioned, I question a possible form using linq that would be more perfomatic. "For" ties with "range", for example, I am aware of.

Thank you all for your collaboration, comments and possible solutions !!!