Created
June 17, 2019 00:20
-
-
Save tmatz/b88a1f3bc41054216f27827d9128d566 to your computer and use it in GitHub Desktop.
Array & List Partitioning
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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