我有IEnumerable<int>
这样,只有更长时间:
5, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 6, 0, 0, 0, 0, 0
现在我想在最后一个非零值之前返回所有元素:
5, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0
似乎sequence.Last()
在这里没有帮助,因为它返回最后一次出现,而不是最后一次出现的索引。
我曾想过要用
var lastIndex = sequence.LastIndex(x=>x!=0);
subsequence = sequence.Take(lastIndex);
这适用于一般情况,但LastIndex
不存在,或
var last = sequence.Last(y=>y!=0);
subsequence = sequence.TakeWhile(x=>x!=last)
哪个适用于该示例,但不适用于一般情况,可能存在重复的非零值。
有什么想法吗?
答案 0 :(得分:5)
你可以试试这个
var allDataBeforeLastNonZero= sequence.GetRange(0,sequence.FindLastIndex(x=>x!=0));
答案 1 :(得分:5)
适用于一般情况,但LastIndex不存在
不,但您可以通过以下方式找到它:
var lastIndex = sequence
.Select((x, i) => new {El = x, Idx = i})
.Where(x => x.El != 0)
.Select(x => x.Idx).Last();
如果您需要与IQueryable<T>
合作,那就是尽可能好。
它有一些问题。一方面,它扫描序列两次,谁说序列甚至允许。我们可以做得更好,但我们必须缓冲,但不一定缓冲整个事情:
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);
}
}
致电sequence.BeforeLastMatch(x => x != 0)
,即可获得5, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0
如果您真的需要它同时处理IEnumerable
和IQueryable
,这也可以处理,但它有点复杂。如果你知道自己只有内存IEnumerable
,请不要打扰。 (此外,一些提供商对不同功能有不同的支持,因此您可能无论如何都被迫执行上面的内存版本):
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());
}
答案 2 :(得分:4)
也许有更有效的方法,但这个方法是可读的,不是吗?
var allBeforeLastNonZero = sequence
.Reverse() // look from the end
.SkipWhile(i => i == 0) // skip the zeros
.Skip(1) // skip last non-zero
.Reverse(); // get original order
答案 3 :(得分:1)
您可以将列表转换为字符串并使用String.Trim
:
var str = String.Join(",", myInputArray);
var result = str.TrimEnd(',', '0').Split(',').Select(x => Convert.ToInt32(x)).ToList();
result.RemoveAt(result.Count - 1);
不得不承认看起来有点难看,但它应该有用。
答案 4 :(得分:1)
IEnumerable<int> source = new List<int> {5,0,0, 4,0,0,3, 0, 0};
List<int> result = new List<int>();
List<int> buffer = new List<int>();
foreach (var i in source)
{
buffer.Add(i);
if (i != 0)
{
result.AddRange(buffer);
buffer.Clear();
}
}