im allgemeinen Fall funktionieren würde, aber last existiert nicht
Nein, aber Sie können es mit finden:
var lastIndex = sequence
.Select((x, i) => new {El = x, Idx = i})
.Where(x => x.El != 0)
.Select(x => x.Idx).Last();
Wenn Sie mit IQueryable<T>
arbeiten müssen, ist das ungefähr so gut, wie Sie bekommen können.
Es hat ein paar Probleme. Zum einen scannt er die Sequenz zweimal durch, und wer sagt, dass die Sequenz das überhaupt zulässt. Wir können es besser machen, aber wir werden Puffer müssen, wenn auch nicht unbedingt die ganze Sache Puffer:
public static IEnumerable<T> BeforeLastMatch<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
return BeforeLastMatchImpl(source, predicate);
}
public static IEnumerable<T> BeforeLastMatchImpl<T>(IEnumerable<T> source, Func<T, bool> predicate)
{
var buffer = new List<T>();
foreach(T item in source)
{
if (predicate(item) && buffer.Count != 0)
{
foreach(T allowed in buffer)
{
yield return allowed;
}
buffer.Clear();
}
buffer.Add(item);
}
}
Anruf sequence.BeforeLastMatch(x => x != 0)
und Sie erhalten 5, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0
Wenn Sie es wirklich brauchen sowohl mit IEnumerable
und IQueryable
zu arbeiten, kann auch gehandhabt werden, aber es ist ein bisschen komplizierter. Ärgere dich nicht, wenn du weißt, dass du nur in-memory IEnumerable
hast.(Auch einige Anbieter haben unterschiedliche Unterstützung für verschiedene Funktionen, so dass Sie gezwungen sein könnten, die In-Memory-Version oben ohnehin zu tun):
private class ElementAndIndex<T>
{
public T Element { get; set; }
public int Index { get; set; }
}
public static IQueryable<T> BeforeLastMatch<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
// If source is actually an in-memory enumerable, the other method will be more efficient,
// so use it instead.
var asEnum = source as EnumerableQuery<T>;
if (asEnum != null && asEnum.Expression.NodeType == ExpressionType.Constant)
{
// On any other IQueryable calling `AsEnumerable()` will force it
// to be loaded into memory, but on an EnumerableQuery it just
// unwraps the wrapped enumerable this will chain back to the
// contained GetEnumerator.
return BeforeLastMatchImpl(source.AsEnumerable(), predicate.Compile()).AsQueryable();
}
// We have a lambda from (T x) => bool, and we need one from
// (ElementAndIndex<T> x) => bool, so build it here.
var param = Expression.Parameter(typeof(ElementAndIndex<T>));
var indexingPredicate = Expression.Lambda<Func<ElementAndIndex<T>, bool>>(
Expression.Invoke(predicate, Expression.Property(param, "Element")),
param
);
return source.Take(// We're going to Take based on the last index this finds.
source
// Elements and indices together
.Select((x, i) => new ElementAndIndex<T>{ Element = x, Index = i})
// The new predicate we created from that passed to us.
.Where(indexingPredicate)
// The last matching element.
.Select(x => x.Index).Last());
}
Dies ist bei weitem die hässlichste Lösung, nimmt aber 'sequence' an sei eine 'Liste'. Anyway +1. –
HimBromBeere
Es ist ein 'IEnumerable' also müsste es zuerst in eine Liste umgewandelt werden. –
Da wirst du _have_ laufen müssen, um ganz IEnumerable zu gehen - kein Schaden, es in Liste umzuwandeln Erstens: – Evk