Skip to content

Instantly share code, notes, and snippets.

@tmatz
Created June 17, 2019 00:20
Show Gist options
  • Save tmatz/b88a1f3bc41054216f27827d9128d566 to your computer and use it in GitHub Desktop.
Save tmatz/b88a1f3bc41054216f27827d9128d566 to your computer and use it in GitHub Desktop.
Array & List Partitioning
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
// Array & List Partition
namespace SoloLearn
{
class Program
{
static void Main(string[] args)
{
var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
// # Basic Usage
//
// Divide to two partitions.
// The rest items are not taken.
//
Demo.Print(() =>
array.Partition(2, 4));
// =>
// {1,2}
// {3,4,5,6}
// # Wildcard
//
// Divide to three partitions.
// To take rest items, wildcard
// (-1) should be added at last.
//
Demo.Print(() =>
array.Partition(2, 4, -1));
// =>
// {1,2}
// {3,4,5,6}
// {7,8}
// # Precedent Wildcard
//
// Divide to two partitions.
// Second takes last two items,
// and first takes precedents.
//
Demo.Print(() =>
array.Partition(-1, 2));
// =>
// {1,2,3,4,5,6}
// {7,8}
// # Multple Wildcard
//
// Divide to five partitions.
// Wildcard partitions takes
// same number of items.
// Previous wildcard take more
// items when indivisible.
//
Demo.Print(() =>
array.Partition(-1, 2, -1, 4, -1));
// =>
// {1}
// {2,3}
// {4}
// {5,6,7,8}
// {}
var list = array.ToList();
// # List
//
// List is also applicative.
//
Demo.Print(() =>
list.Partition(-1, -1));
// =>
// {1,2,3,4}
// {5,6,7,8}
// # Enumerable
Demo.Print(() =>
Enumerable.Range(1, 8).Partition(-1, -1));
// =>
// {1,2,3,4}
// {5,6,7,8}
// # Sift
Demo.Print(() =>
Enumerable.Range(1, 20)
.Sift(
x => x % 5 == 0,
x => x % 3 == 0,
x => x % 2 == 0));
// =>
// {5,10,15,20}
// {3,6,9,12,18}
// {2,4,8,14,16}
}
}
static class PartitionUtility
{
private const int Wildcard = -1;
public static IEnumerable<T>[] Partition<T>(this T[] source, params int[] count)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var segments = Partition(source.Length, count);
return segments
.Select(s => (IEnumerable<T>)(new ArraySegment<T>(source, s.Offset, s.Count)))
.ToArray();
}
public static IEnumerable<T>[] Partition<T>(this IReadOnlyList<T> source, params int[] count)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var segments = Partition(source.Count, count);
return segments
.Select(s => source
.Skip(s.Offset)
.Take(s.Count)
.ToList())
.ToArray();
}
public static IEnumerable<T>[] Partition<T>(this IEnumerable<T> source, params int[] count)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return Partition(source.ToList(), count);
}
private static (int Offset, int Count)[] Partition(int totalCount, params int[] count)
{
if (count == null)
{
throw new ArgumentNullException(nameof(count));
}
if (count.Length == 0)
{
throw new ArgumentException("at least one item is required.", nameof(count));
}
if (count.Any(x => x < Wildcard))
{
throw new ArgumentOutOfRangeException(nameof(count));
}
int totalSpecifiedCount = count.Select(x => Math.Max(0, x)).Sum();
int totalWildcardCount = Math.Max(0, totalCount - totalSpecifiedCount);
int numWildcard = count.Count(x => x == Wildcard);
var result = new (int Offset, int Count)[count.Length];
int index = 0;
int offset = 0;
foreach (var c in count)
{
var c_ = c;
if (c_ == Wildcard)
{
c_ = (totalWildcardCount + numWildcard - 1) / numWildcard;
numWildcard--;
totalWildcardCount -= c_;
}
c_ = Math.Min(totalCount - offset, c_);
result[index++] = (offset, c_);
offset += c_;
}
return result;
}
public static IEnumerable<T>[] Sift<T>(this IEnumerable<T> source, params Predicate<T>[] predicate)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate == null || predicate.Any(x => x == null))
{
throw new ArgumentNullException(nameof(predicate));
}
var result = predicate
.Select(_ => new List<T>())
.ToArray();
foreach (var x in source)
{
for (int i = 0; i < predicate.Length; i++)
{
if (predicate[i](x))
{
result[i].Add(x);
break;
}
}
}
return result;
}
}
static class Demo
{
public static void Print(Expression<Func<IEnumerable<int>[]>> exp)
{
Console. WriteLine($"{ToString(exp.Body)} => {{");
var func = exp.Compile();
foreach (var p in func())
{
Console.Write(" ");
Console. WriteLine(ToString(p));
}
Console.WriteLine("}");
Console.WriteLine();
}
public static void Print<T>(Expression<Func<T>> exp)
{
Console. WriteLine($"{ToString(exp.Body)} => {{");
var func = exp.Compile();
Console. WriteLine(ToString(func()));
Console.WriteLine();
}
public static string ToString(object o)
{
switch (o)
{
case MethodCallExpression c:
return ToString(c);
case MemberExpression m:
return ToString(m);
case IEnumerable<int> e:
return ToString(e);
default:
return o.ToString()
.Replace("new [] ", "")
.Replace("{ ", "{")
.Replace(" }", "}")
.Replace(", ", ",");
}
}
static string ToString(MethodCallExpression e)
{
var instance = ToString(e.Arguments[0]);
var method = e.Method.Name;
var args = ToString(e.Arguments[1]);
return $"{instance}.{method}({args})";
}
static string ToString(MemberExpression e)
{
object owner;
switch (e.Expression)
{
case ConstantExpression c:
owner = c.Value;
break;
default:
throw new NotImplementedException();
}
switch (e.Member)
{
case FieldInfo f:
return ToString(f.GetValue(owner));
default:
throw new NotImplementedException();
}
}
static string RemoveBraces(string str)
{
var start = str.IndexOf('{');
var end = str.LastIndexOf('}');
return str.Substring(start + 1, end - start - 1).Replace(" ", "");
}
static string ToString<T>(IEnumerable<T> seq)
where T : IFormattable
{
var str = string.Join(",", seq.Select(x => x.ToString()));
return $"{{{str}}}";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment