C# - Elegant Way of Partitioning a List

C# - elegant way of partitioning a list?

Here is an extension method that will do what you want:

public static IEnumerable<List<T>> Partition<T>(this IList<T> source, Int32 size)
{
for (int i = 0; i < (source.Count / size) + (source.Count % size > 0 ? 1 : 0); i++)
yield return new List<T>(source.Skip(size * i).Take(size));
}

Edit: Here is a much cleaner version of the function:

public static IEnumerable<List<T>> Partition<T>(this IList<T> source, Int32 size)
{
for (int i = 0; i < Math.Ceiling(source.Count / (Double)size); i++)
yield return new List<T>(source.Skip(size * i).Take(size));
}

What is the best way to partition a collection into smaller collections

I use this extension method for my code

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> t, int size)
{
while (t.Any())
{
yield return t.Take(size);
t = t.Skip(size);
}
}

What is the simplest way to partition a list based on a criteria?

Two LINQ statements would do:

var nameNotNull = objectList.Where(o => !string.IsNullOrEmpty(o.Name));
var nameNull = objectList.Where(o => string.IsNullOrEmpty(o.Name));

Of course, you could use GroupBy, or a more efficient foreach statement.

To show the foreach option:

List<MyObject> nameNotNull = new List<MyObject>();
List<MyObject> nameNull = new List<MyObject>();

foreach (MyObject o in objectList)
{
if (!string.IsNullOrEmpty(o.Name))
{
nameNotNull.Add(o);
}
else
{
nameNull.Add(o);
}
}

Partition list by delimiter

Directly in Linq, I think it will be hard, but you can create a custom operator. Maybe something like this :

List<String> test = new List<String>() { "1", "8", ";", "2", "7", "42", ";", "3" };
var restul = test.StrangePartition(";");

with :

public static class Helper
{
public static IEnumerable<IEnumerable<T>> StrangePartition<T>(this IEnumerable<T> source, T partitionKey)
{
List<List<T>> partitions = new List<List<T>>();
List<T> partition = new List<T>();
foreach (T item in source)
{

if (item.Equals(partitionKey))
{
partitions.Add(partition);
partition = new List<T>();
}
else
{
partition.Add(item);
}
}
partitions.Add(partition);
return partitions;
}
}

Efficiently partition list into chunks of a fixed size

Since I'm using the partitions locally, I ended up chosing to invert the situation: instead of passing the partitions to the action using it, I can pass the action to the function doing the partitioning.

Public Sub DoForPartition(Of T)(source As IEnumerable(Of T), 
size As Integer,
doThis As Action(Of IEnumerable(Of T)))

Dim partition(size - 1) As T
Dim count = 0

For Each t in source
partition(count) = t
count += 1

If count = size Then
doThis(partition)
count = 0
End If
Next

If count > 0 Then
Array.Resize(partition, count)
doThis(partition)
End If
End Sub

This function avoids looping through the source multiple times and the only memory overhead is the size of the partition (instead of the entire source like some of the other options). I didn't write this function myself, but adapted a similarly looking C# function from this answer.

This looks like a much better algorithm than the one in my question.

LINQ Partition List into Lists of 8 members

Use the following extension method to break the input into subsets

public static class IEnumerableExtensions
{
public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
{
List<T> toReturn = new List<T>(max);
foreach(var item in source)
{
toReturn.Add(item);
if (toReturn.Count == max)
{
yield return toReturn;
toReturn = new List<T>(max);
}
}
if (toReturn.Any())
{
yield return toReturn;
}
}
}

How to divide an array into two?

Assuming you just need to split an array (or List) of elements into first/second half - usually one would use Enumerable.Take/Enumerable.Skip followed by Enumerable.ToArray to convert to array if necessary:

string[] Player1Array = GetImages().Take(20).ToArray();
string[] Player2Array = GetImages().Skip(20).ToArray();

When to use Partitioner class?

The Partitioner class is used to make parallel executions more chunky. If you have a lot of very small tasks to run in parallel the overhead of invoking delegates for each may be prohibitive. By using Partitioner, you can rearrange the workload into chunks and have each parallel invocation work on a slightly larger set. The class abstracts this feature and is able to partition based on the actual conditions of the dataset and available cores.

Example: Imagine you want to run a simple calculation like this in parallel.

Parallel.ForEach(Input, (value, loopState, index) => { Result[index] = value*Math.PI; });

That would invoke the delegate for each entry in Input. Doing so would add a bit of overhead to each. By using Partitioner we can do something like this

Parallel.ForEach(Partitioner.Create(0, Input.Length), range => {
for (var index = range.Item1; index < range.Item2; index++) {
Result[index] = Input[index]*Math.PI;
}
});

This will reduce the number of invokes as each invoke will work on a larger set. In my experience this can boost performance significantly when parallelizing very simple operations.

Split List into Sublists with LINQ

Try the following code.

public static List<List<T>> Split<T>(IList<T> source)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / 3)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}

The idea is to first group the elements by indexes. Dividing by three has the effect of grouping them into groups of 3. Then convert each group to a list and the IEnumerable of List to a List of Lists

How to do partitioning in combination with Service Fabric Remoting

Here's a video that covers this subject and here's a working code sample that goes with it.

  • use a (proper e.g. FNV) hash algorithm to get the hash of username, use the long result to determine an Int64RangePartition by its key.
  • use a gateway that does the hashing for you (e.g. api management, or a custom stateless service) and forwards the call
  • you could use customer headers to pass the username. (the sample doesn't)


Related Topics



Leave a reply



Submit