// Ms-PL License // linq.js C# backport // http://linqjs.codeplex.com/ using System.Collections.Generic; using System.Text; using System.Collections; namespace System.Linq { public static class LinqJS { #region Generating Methods public static IEnumerable Choice(params T[] values) { var rand = new Random(); while (true) { yield return values[rand.Next(values.Length)]; } } public static IEnumerable Cycle(params T[] values) { while (true) { foreach (var item in values) { yield return item; } } } public static IEnumerable Range(int start, int count) { return Range(start, count, 1); } public static IEnumerable Range(int start, int count, int step) { return ToInfinity(start, step).Take(count); } public static IEnumerable RangeDown(int start, int count) { return RangeDown(start, count, 1); } public static IEnumerable RangeDown(int start, int count, int step) { return ToNegativeInfinity(start, step).Take(count); } public static IEnumerable RangeTo(int from, int to) { return RangeTo(from, to, 1); } public static IEnumerable RangeTo(int from, int to, int step) { return (from < to) ? ToInfinity(from, step).TakeWhile(i => i <= to) : ToNegativeInfinity(from, step).TakeWhile(i => i >= to); } public static IEnumerable Return(T value) { return Enumerable.Repeat(value, 1); } public static IEnumerable Generate(Func func) { while (true) { yield return func(); } } public static IEnumerable Generate(Func func, int count) { return Generate(func).Take(count); } public static IEnumerable ToInfinity() { return ToInfinity(0); } public static IEnumerable ToInfinity(int start) { return ToInfinity(start, 1); } public static IEnumerable ToInfinity(int start, int step) { while (true) { yield return start; start += step; } } public static IEnumerable ToNegativeInfinity() { return ToNegativeInfinity(0); } public static IEnumerable ToNegativeInfinity(int start) { return ToNegativeInfinity(start, 1); } public static IEnumerable ToNegativeInfinity(int start, int step) { return ToInfinity(start, -step); } public static IEnumerable Unfold(T seed, Func func) { yield return seed; var value = seed; while (true) { value = func(value); yield return value; } } #endregion #region Projection and Filtering Methods public static IEnumerable CascadeDepthFirst(this IEnumerable source, Func> selector) { return source.CascadeDepthFirst(selector, t => t); } public static IEnumerable CascadeDepthFirst(this IEnumerable source, Func> selector, Func resultSelector) { return source.CascadeDepthFirst(selector, (t, _) => resultSelector(t)); } public static IEnumerable CascadeDepthFirst(this IEnumerable source, Func> selector, Func resultSelector) { return source.DepthFirstSearch(selector, 0).Select(kvp => resultSelector(kvp.Key, kvp.Value)); } private static IEnumerable> DepthFirstSearch(this IEnumerable source, Func> selector, int depth) { foreach (var item in source) { yield return new KeyValuePair(item, depth); foreach (var subitem in selector(item).DepthFirstSearch(selector, depth + 1)) { yield return subitem; } } } public static IEnumerable CascadeBreadthFirst(this IEnumerable source, Func> selector) { return source.CascadeBreadthFirst(selector, t => t); } public static IEnumerable CascadeBreadthFirst(this IEnumerable source, Func> selector, Func resultSelector) { return source.CascadeBreadthFirst(selector, (t, _) => resultSelector(t)); } /// public static IEnumerable CascadeBreadthFirst(this IEnumerable source, Func> selector, Func resultSelector) { var depth = 0; var buffer = source.ToArray(); while (buffer.Any()) { foreach (var item in buffer) yield return resultSelector(item, depth); buffer = buffer.SelectMany(selector).ToArray(); depth++; } } public static IEnumerable Flatten(this IEnumerable source) { foreach (var item in source) { var subsource = item as IEnumerable; if (subsource != null) { foreach (var subitem in subsource.Flatten()) { yield return subitem; } } else { yield return (T)item; } } } public static IEnumerable Pairwise(this IEnumerable source, Func selector) { var isFirst = true; var prev = default(T); foreach (var item in source) { if (isFirst) { prev = item; isFirst = false; continue; } yield return selector(prev, item); prev = item; } } public static IEnumerable Scan(this IEnumerable source, Func func) { var isFirst = true; var value = default(T); foreach (var item in source) { if (isFirst) { isFirst = false; value = item; yield return value; continue; } value = func(value, item); yield return value; } } public static IEnumerable Scan(this IEnumerable source, TA seed, Func func) { return source.Scan(seed, func, x => x); } public static IEnumerable Scan(this IEnumerable source, TA seed, Func func, Func resultSelector) { yield return resultSelector(seed); var value = seed; foreach (var item in source) { value = func(value, item); yield return resultSelector(value); } } //Obsolete .NET 4] //public static IEnumerable Zip(this IEnumerable source, IEnumerable source2, Func selector) //{ // using (var e1 = source.GetEnumerator()) // using (var e2 = source2.GetEnumerator()) // { // while (e1.MoveNext() && e2.MoveNext()) // { // yield return selector(e1.Current, e2.Current); // } // } //} #endregion #region Set Methods public static IEnumerable Insert(this IEnumerable source, int index, IEnumerable second) { var count = 0; foreach (var item in source) { if (count++ == index) { foreach (var subitem in second) yield return subitem; } yield return item; } } #endregion #region Ordering Methods public static IEnumerable Shuffle(this IEnumerable source) { var rand = new Random(); return source.OrderBy(_ => rand.Next()); } #endregion #region Paging Methods public static int IndexOf(this IEnumerable source, T value) { return source.IndexOf(value, EqualityComparer.Default); } public static int IndexOf(this IEnumerable source, T value, EqualityComparer comparer) { var index = 0; foreach (var item in source) { if (comparer.Equals(value, item)) return index; index++; } return -1; } public static int LastIndexOf(this IEnumerable source, T value) { return source.LastIndexOf(value, EqualityComparer.Default); } public static int LastIndexOf(this IEnumerable source, T value, EqualityComparer comparer) { var index = 0; var result = -1; foreach (var item in source) { if (comparer.Equals(value, item)) result = index; index++; } return result; } #endregion #region Convert Methods public static string ToStringJoin(this IEnumerable source) { return source.ToStringJoin(""); } public static string ToStringJoin(this IEnumerable source, string separator) { var index = 0; return source.Aggregate(new StringBuilder(), (sb, v) => (index++ == 0) ? sb.Append(v) : sb.AppendFormat("{0}{1}", separator, v)) .ToString(); } public static string ToStringJoin(this IEnumerable source, string separator, Func selector) { return source.Select(selector).ToStringJoin(separator); } #endregion #region Action Methods public static IEnumerable Do(this IEnumerable source, Action action) { return source.Do((v, i) => action(v)); } public static IEnumerable Do(this IEnumerable source, Action action) { var index = 0; foreach (var item in source) { action(item, index++); yield return item; } } public static void ForEach(this IEnumerable source, Action action) { source.ForEach((v, i) => action(v)); } public static void ForEach(this IEnumerable source, Action action) { source.Do(action).Force(); } public static void Write(this IEnumerable source) { source.Write(""); } public static void Write(this IEnumerable source, string separator) { source.ForEach((t, index) => Console.Write((index == 0) ? t.ToString() : separator + t)); } public static void Write(this IEnumerable source, string separator, Func selector) { source.Select(selector).Write(separator); } public static void WriteLine(this IEnumerable source) { source.ForEach(t => Console.WriteLine(t)); } public static void WriteLine(this IEnumerable source, Func selector) { source.Select(selector).WriteLine(); } #endregion #region For Debug Methods public static void Force(this IEnumerable source) { using (var e = source.GetEnumerator()) { while (e.MoveNext()) ; } } public static IEnumerable Trace(this IEnumerable source) { return source.Do(t => Diagnostics.Trace.WriteLine(t)); } #endregion } }