Skip to content

Instantly share code, notes, and snippets.

@Manuel-S
Last active December 27, 2017 00:09
Show Gist options
  • Select an option

  • Save Manuel-S/fd09aebd1fefb0b5d482dd2123043e2d to your computer and use it in GitHub Desktop.

Select an option

Save Manuel-S/fd09aebd1fefb0b5d482dd2123043e2d to your computer and use it in GitHub Desktop.
LINQ Extensions: WhereProperties, FullTextSearch, DistinctKey, Once, ReplaceMatchingSequence
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