From 372bf2b13888d88aebc8662d67f5e6355261c953 Mon Sep 17 00:00:00 2001 From: Logan Barnett Date: Mon, 22 Apr 2013 12:01:30 -0700 Subject: [PATCH] Various additional helpers Added int.Times(Action) where the int is the index Other stuff I don't recall (are we that out of sync?) --- src/NaturalLanguage.cs | 347 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 335 insertions(+), 12 deletions(-) diff --git a/src/NaturalLanguage.cs b/src/NaturalLanguage.cs index 00f9217..c103ab2 100644 --- a/src/NaturalLanguage.cs +++ b/src/NaturalLanguage.cs @@ -1,39 +1,91 @@ using System; +using System.Linq.Expressions; +using System.Linq; using System.Collections; using System.Collections.Generic; +using System.Text; namespace GoodStuff { namespace NaturalLanguage { - public delegate void TwoParamDelegate(T value, int i); + + public delegate bool Predicate(T1 item1, T2 item2); public static class IntExtensions { /// /// Calls the provided callback action repeatedly. /// /// - /// Used to replace traditional for loop iteration. - /// 5.Times(i => Console.WriteLine(i)); + /// Used to invoke an action a fixed number of times. + /// + /// 5.Times(() => Console.WriteLine("Hey!")); /// /// is the equivalent of /// /// for(var i = 0; i < 5; i++) { - /// Console.WriteLine(i); + /// Console.WriteLine("Hey!"); + /// } + /// + public static void Times(this int iterations, Action callback) { + for(var i = 0; i < iterations; ++i) { + callback(); + } + } + + /// + /// Calls the provided callback action repeatedly. + /// + /// + /// Used to invoke an action a fixed number of times. + /// + /// 5.Times(i => Console.WriteLine("This is {0}", i)); + /// + /// is the equivalent of + /// + /// for(var i = 0; i < 5; i++) { + /// Console.WriteLine("This is {0}", i); /// } /// - public static void Times(this int iterations, Action callback) { for(var i = 0; i < iterations; ++i) { callback(i); } } + /// + /// Iterates from the start up to the given end value inclusive, calling the provided callback with each value in the sequence. + /// + /// + /// Used to iterate from a start value to a target value + /// + /// 0.UpTo(5, i => Console.WriteLine(i)); + /// + /// is the equivalent of + /// + /// for(var i = 0; i <= 5; i++) { + /// Console.WriteLine(i); + /// } + /// public static void UpTo(this int value, int endValue, Action callback) { for(var i = value; i <= endValue; ++i) { callback(i); } } + /// + /// Iterates from the start down to the given end value inclusive, calling the provided callback with each value in the sequence. + /// + /// + /// Used to iterate from a start value to a target value + /// + /// 5.DownTo(0, i => Console.WriteLine(i)); + /// + /// is the equivalent of + /// + /// for(var i = 5; i >= 0; i++) { + /// Console.WriteLine(i); + /// } + /// public static void DownTo(this int value, int endValue, Action callback) { for(var i = value; i >= endValue; --i) { callback(i); @@ -41,27 +93,298 @@ public static void DownTo(this int value, int endValue, Action callback) { } } + public static class FloatExtensions { + /// + /// Maps a value in one range to the equivalent value in another range. + /// + public static float MapToRange(this float value, float range1Min, float range1Max, float range2Min, float range2Max) { + return MapToRange(value, range1Min, range1Max, range2Min, range2Max, true); + } + + /// + /// Maps a value in one range to the equivalent value in another range. Clamps the value to be valid within the range if clamp is specified as true. + /// + public static float MapToRange(this float value, float range1Min, float range1Max, float range2Min, float range2Max, bool clamp) { + + value = range2Min + ((value - range1Min) / (range1Max - range1Min)) * (range2Max - range2Min); + + if(clamp) { + if(range2Min < range2Max) { + if(value > range2Max) value = range2Max; + if(value < range2Min) value = range2Min; + } + // Range that go negative are possible, for example from 0 to -1 + else { + if(value > range2Min) value = range2Min; + if(value < range2Max) value = range2Max; + } + } + return value; + } + } public static class IEnumerableExtensions { /// - /// Iterates over each element in the IEnumerable, passing in the element to the provided callback + /// Iterates over each element in the IEnumerable, passing in the element to the provided callback. /// - public static void Each(this IEnumerable iteratable, Action callback) { - foreach(var value in iteratable) { + public static void Each(this IEnumerable iterable, Action callback) { + foreach(var value in iterable) { callback(value); } } /// - /// Iterates over each element in the IEnumerable, passing in the element and the index to the provided callback + /// Iterates over each element in the IEnumerable, passing in the element to the provided callback. Since the IEnumerable is + /// not generic, a type must be specified as a type parameter to Each. /// - public static void EachWithIndex(this IEnumerable iteratable, TwoParamDelegate callback) { + /// + /// IEnumerable myCollection = new List(); + /// ... + /// myCollection.Each(i => Debug.Log("i: " + i)); + /// + public static void Each(this IEnumerable iterable, Action callback) { + foreach(T value in iterable) { + callback(value); + } + } + +// /// +// /// Iterates over each element in the IEnumerable, passing in the element to the provided callback. +// /// +// public static void Each(this IEnumerable iterable, Action callback) { +// foreach(object value in iterable) { +// callback(value); +// } +// } + + /// + /// Iterates over each element in the IEnumerable, passing in the element and the index to the provided callback. + /// + public static void EachWithIndex(this IEnumerable iterable, Action callback) { var i = 0; - foreach(var value in iteratable) { + foreach(var value in iterable) { callback(value, i); ++i; } } + + /// + /// Iterates over each element in the IEnumerable, passing in the element and the index to the provided callback. + /// + public static void EachWithIndex(this IEnumerable iterable, Action callback) { + var i = 0; + foreach(T value in iterable) { + callback(value, i); + ++i; + } + } + + /// + /// Iterates over each element in both the iterable1 and iterable2 collections, passing in the current element of each collection into the provided callback. + /// + public static void InParallelWith(this IEnumerable iterable1, IEnumerable iterable2, Action callback) { + if(iterable1.Count() != iterable2.Count()) throw new ArgumentException(string.Format("Both IEnumerables must be the same length, iterable1: {0}, iterable2: {1}", iterable1.Count(), iterable2.Count())); + + var i1Enumerator = iterable1.GetEnumerator(); + var i2Enumerator = iterable2.GetEnumerator(); + + while(i1Enumerator.MoveNext()) { + i2Enumerator.MoveNext(); + callback(i1Enumerator.Current, i2Enumerator.Current); + } + } + + /// + /// Iterates over each element in both the iterable1 and iterable2 collections, passing in the current element of each collection into the provided callback. + /// + public static void InParallelWith(this IEnumerable iterable1, IEnumerable iterable2, Action callback) { + var i1Enumerator = iterable1.GetEnumerator(); + var i2Enumerator = iterable2.GetEnumerator(); + var i1Count = 0; + var i2Count = 0; + while(i1Enumerator.MoveNext()) ++i1Count; + while(i2Enumerator.MoveNext()) ++i2Count; + if(i1Count != i2Count) throw new ArgumentException(string.Format("Both IEnumerables must be the same length, iterable1: {0}, iterable2: {1}", i1Count, i2Count)); + + i1Enumerator.Reset(); + i2Enumerator.Reset(); + while(i1Enumerator.MoveNext()) { + i2Enumerator.MoveNext(); + callback(i1Enumerator.Current, i2Enumerator.Current); + } + } + + public static bool IsEmpty(this IEnumerable iterable) { + return iterable.Count() == 0; + } + + public static bool IsEmpty(this IEnumerable iterable) { + // MoveNext returns false if we are at the end of the collection + return !iterable.GetEnumerator().MoveNext(); + } + } + + public static class ArrayExtensions { + /// + /// Returns the first index in the array where the target exists. If the target cannot be found, returns -1. + /// + public static int IndexOf(this T[] array, T target) { + for(var i = 0; i < array.Length; ++i) { + if(array[i].Equals(target)) return i; + } + return -1; + } + } + + public static class ListExtensions { + //TODO: Fix issue of multiple threads accessing in the same millisecond + [ThreadStatic] + static System.Random randomNumberGenerator = new Random(DateTime.Now.Millisecond); + + /// + /// Returns a sub-section of the current list, starting at the specified index and continuing to the end of the list. + /// + public static List FromIndexToEnd(this List list, int start) { + return list.GetRange(start, list.Count - start); + } + + /// + /// Returns the first index in the List where the target exists. If the target cannot be found, returns -1. + /// + public static int IndexOf(this List list, T target) { + for(var i = 0; i < list.Count; ++i) { + if(list[i].Equals(target)) return i; + } + return -1; + } + + /// Returns a randomly selected item from List + public static T RandomElement(this List list) { + if(list.IsEmpty()) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty list"); + + return list[randomNumberGenerator.Next(list.Count)]; + } + + /// Returns a randomly selected item from List determined by a float array of weights + public static T RandomElement(this List list, float[] weights) { + return list.RandomElement(weights.ToList()); + } + + /// Returns a randomly selected item from List determined by a List of weights + public static T RandomElement(this List list, List weights) { + if(list.IsEmpty()) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty list"); + if(list.Count() != weights.Count()) throw new IndexOutOfRangeException("List of weights must be the same size as input list"); + + var randomWeight = randomNumberGenerator.NextDouble() * weights.Sum(); + var totalWeight = 0f; + var index = weights.FindIndex(weight => { + totalWeight += weight; + return randomWeight <= totalWeight; + }); + + return list[index]; + } + + public static List Shuffle(this List list) { + return list.OrderBy(e => randomNumberGenerator.Next()).ToList(); + } + } + + public static class DictionaryExtensions { + /// + /// Iterates over a Dictionary passing in both the key and value to the provided callback. + /// + public static void Each(this Dictionary dictionary, Action callback) { + foreach(var keyValuePair in dictionary) { + callback(keyValuePair.Key, keyValuePair.Value); + } + } + + public static void RemoveAll(this Dictionary dictionary, Predicate callback) { + var keysToRemove = new List(); + foreach(var keyValuePair in dictionary) { + if(callback(keyValuePair.Key, keyValuePair.Value)) { + keysToRemove.Add(keyValuePair.Key); + } + } + + foreach(var key in keysToRemove) { + dictionary.Remove(key); + } + } + } + + public static class StringExtensions { + public static T ToEnum(this string enumValueName) { + return (T)Enum.Parse(typeof(T), enumValueName); + } + + public static T ToEnum(this string enumValueName, bool ignoreCase) { + return (T)Enum.Parse(typeof(T), enumValueName, ignoreCase); + } + + public static string Last(this string value, int count) { + if(count > value.Length) throw new ArgumentOutOfRangeException(string.Format("Cannot return more characters than exist in the string (wanted {0} string contains {1}", count, value.Length)); + + return value.Substring(value.Length - count, count); + } + + public static string SnakeCase(this string camelizedString) { + var parts = new List(); + var currentWord = new StringBuilder(); + + foreach(var c in camelizedString) { + if (char.IsUpper(c) && currentWord.Length > 0) { + parts.Add(currentWord.ToString()); + currentWord = new StringBuilder(); + } + currentWord.Append(char.ToLower(c)); + } + + if(currentWord.Length > 0) { + parts.Add(currentWord.ToString()); + } + + return string.Join("_", parts.ToArray()); + } + + public static string Capitalize(this string word) { + return word.Substring(0, 1).ToUpper() + word.Substring(1); + } + } + + public static class TypeExtensions { + /// + /// Returns an array of all concrete subclasses of the provided type. + /// + public static Type[] Subclasses(this Type type) { + var typeList = new List(); + foreach(var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) { + try { + typeList.AddRange(assembly.GetTypes()); + } + catch(System.Reflection.ReflectionTypeLoadException) { + // how do we log this in GoodStuff? + } + } + return typeList.Where(t => t.IsSubclassOf(type) && !t.IsAbstract).ToArray(); + } + + /// + /// Returns an array of the provided type and all concrete subclasses of that type. + /// + public static Type[] TypeAndSubclasses(this Type type) { + var typeList = new List(); + foreach(var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) { + try { + typeList.AddRange(assembly.GetTypes()); + } + catch(System.Reflection.ReflectionTypeLoadException) { + // how do we log this in GoodStuff? + } + } + return typeList.Where(t => (t == type || t.IsSubclassOf(type)) && !t.IsAbstract).ToArray(); + } } } -} \ No newline at end of file +}