Last active
December 27, 2017 00:09
-
-
Save Manuel-S/fd09aebd1fefb0b5d482dd2123043e2d to your computer and use it in GitHub Desktop.
LINQ Extensions: WhereProperties, FullTextSearch, DistinctKey, Once, ReplaceMatchingSequence
This file contains hidden or 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; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Linq.Expressions; | |
| public static partial class LinqExtensions | |
| { | |
| public static IEnumerable<T> WhereProperties<T, P>(this IEnumerable<T> query, Func<IEnumerable<P>, bool> pred) | |
| => query.Where(item => | |
| pred( | |
| item | |
| .GetType() | |
| .GetProperties() | |
| .Where(x => x.PropertyType == typeof(P)) | |
| .Select(prop => (P)prop.GetValue(item)) | |
| ) | |
| ); | |
| public static IEnumerable<T> FullTextSearch<T>(this IEnumerable<T> query, string search, StringComparison comparison = StringComparison.CurrentCulture) | |
| => query.WhereProperties<T, string>(s => s.Any(si => si.IndexOf(search, comparison) >= 0)); | |
| public static IEnumerable<T> DistinctKey<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) | |
| { | |
| var knownKeys = new HashSet<TKey>(EqualityComparer<TKey>.Default); | |
| return source.Where(e => knownKeys.Add(keySelector(e))); | |
| } | |
| public static IEnumerable<T> Once<T>(this IEnumerable<T> source) | |
| => new StatefulEnumerable<T>(source); | |
| private class StatefulEnumerable<T> : IEnumerable<T> | |
| { | |
| private IEnumerator<T> enumerator; | |
| public StatefulEnumerable(IEnumerable<T> source) | |
| => enumerator = source.GetEnumerator(); | |
| public IEnumerator<T> GetEnumerator() => enumerator; | |
| IEnumerator IEnumerable.GetEnumerator() => enumerator; | |
| } | |
| public static IEnumerable<T> ReplaceMatchingSequence<T>(this IEnumerable<T> source, IEnumerable<T> elements, Func<IEnumerable<T>, IEnumerable<T>> replacer) | |
| { | |
| return ReplaceMatchingSequence(source, elements.Select(x => (Func<T, bool>)(y => y.Equals(x))), replacer); | |
| } | |
| public static IEnumerable<T> ReplaceMatchingSequence<T>(this IEnumerable<T> source, IEnumerable<Func<T, bool>> predicates, Func<IEnumerable<T>, IEnumerable<T>> replacer) | |
| { | |
| if (source == null) | |
| throw new ArgumentNullException(nameof(source)); | |
| if (predicates == null) | |
| throw new ArgumentNullException(nameof(predicates)); | |
| var p = predicates.ToArray(); | |
| if (p.Length == 0) | |
| throw new ArgumentException("Count of predicates is zero."); | |
| if (replacer == null) | |
| throw new ArgumentNullException(nameof(replacer)); | |
| return ReplaceMatchingSequenceInner(source, predicates, replacer); | |
| } | |
| private static IEnumerable<T> ReplaceMatchingSequenceInner<T>(this IEnumerable<T> source, IEnumerable<Func<T, bool>> predicates, Func<IEnumerable<T>, IEnumerable<T>> replacer) | |
| { | |
| var p = predicates.ToArray(); | |
| var matchingIndex = -1; | |
| var buffer = new List<T>(); | |
| foreach (var e in source) | |
| { | |
| // matched next predicate | |
| if (p[matchingIndex + 1](e)) | |
| { | |
| matchingIndex++; | |
| // add item to buffer | |
| buffer.Add(e); | |
| // all predicates matched | |
| if (matchingIndex + 1 == p.Length) | |
| { | |
| matchingIndex = -1; | |
| foreach (var r in replacer(buffer)) | |
| { | |
| yield return r; | |
| } | |
| buffer.Clear(); | |
| } | |
| } | |
| else | |
| { | |
| // no match, so clear buffer if any and reset index | |
| foreach (var i in buffer) | |
| yield return i; | |
| buffer.Clear(); | |
| matchingIndex = -1; | |
| // then return current item | |
| yield return e; | |
| } | |
| } | |
| // finished going through source, send out the buffer if any | |
| foreach (var l in buffer) | |
| yield return l; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment