How to Take All But the Last Element in a Sequence Using Linq

How to take all but the last element in a sequence using LINQ?

I don't know a Linq solution - But you can easily code the algorithm by yourself using generators (yield return).

public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source) {
var it = source.GetEnumerator();
bool hasRemainingItems = false;
bool isFirst = true;
T item = default(T);

do {
hasRemainingItems = it.MoveNext();
if (hasRemainingItems) {
if (!isFirst) yield return item;
item = it.Current;
isFirst = false;
}
} while (hasRemainingItems);
}

static void Main(string[] args) {
var Seq = Enumerable.Range(1, 10);

Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
Console.WriteLine(string.Join(", ", Seq.TakeAllButLast().Select(x => x.ToString()).ToArray()));
}

Or as a generalized solution discarding the last n items (using a queue like suggested in the comments):

public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
var it = source.GetEnumerator();
bool hasRemainingItems = false;
var cache = new Queue<T>(n + 1);

do {
if (hasRemainingItems = it.MoveNext()) {
cache.Enqueue(it.Current);
if (cache.Count > n)
yield return cache.Dequeue();
}
} while (hasRemainingItems);
}

static void Main(string[] args) {
var Seq = Enumerable.Range(1, 4);

Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
Console.WriteLine(string.Join(", ", Seq.SkipLastN(3).Select(x => x.ToString()).ToArray()));
}

Drop the last item with LINQ

For .NET Core 2+ and .NET Standard 2.1 (planned), you can use .SkipLast(1).

For other platforms, you could write your own LINQ query operator (that is, an extension method on IEnumerable<T>), for example:

static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
for (var value = e.Current; e.MoveNext(); value = e.Current)
{
yield return value;
}
}
}
}

Unlike other approaches such as xs.Take(xs.Count() - 1), the above will process a sequence only once.

Don't take last record in linq C#

What about this:

static public IEnumerable<T> SkipLast<T>(this IEnumerable<T> data, int count)
{
if (data == null || count < 0) yield break;

Queue<T> queue = new Queue<T>(data.Take(count));

foreach (T item in data.Skip(count))
{
queue.Enqueue(item);
yield return queue.Dequeue();
}
}

Update

With help from some reviews an optimized version building on the same idea could be:

static public IEnumerable<T> SkipLast<T>(this IEnumerable<T> data, int count)
{
if (data == null) throw new ArgumentNullException(nameof(data));
if (count <= 0) return data;

if (data is ICollection<T> collection)
return collection.Take(collection.Count - count);

IEnumerable<T> Skipper()
{
using (var enumer = data.GetEnumerator())
{
T[] queue = new T[count];
int index = 0;

while (index < count && enumer.MoveNext())
queue[index++] = enumer.Current;

index = -1;
while (enumer.MoveNext())
{
index = (index + 1) % count;
yield return queue[index];
queue[index] = enumer.Current;
}
}
}

return Skipper();
}

how to take all array elements except last element in C#

var remStrings = queries.Take(queries.Length - 1);

No need to Reverse and Skip. Just take one less element than there are in the array.

If you really wanted the elements in the reverse order, you could tack on a .Reverse() to the end.

Using Linq to get the last N elements of a collection?

collection.Skip(Math.Max(0, collection.Count() - N));

This approach preserves item order without a dependency on any sorting, and has broad compatibility across several LINQ providers.

It is important to take care not to call Skip with a negative number. Some providers, such as the Entity Framework, will produce an ArgumentException when presented with a negative argument. The call to Math.Max avoids this neatly.

The class below has all of the essentials for extension methods, which are: a static class, a static method, and use of the this keyword.

public static class MiscExtensions
{
// Ex: collection.TakeLast(5);
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int N)
{
return source.Skip(Math.Max(0, source.Count() - N));
}
}

A brief note on performance:

Because the call to Count() can cause enumeration of certain data structures, this approach has the risk of causing two passes over the data. This isn't really a problem with most enumerables; in fact, optimizations exist already for Lists, Arrays, and even EF queries to evaluate the Count() operation in O(1) time.

If, however, you must use a forward-only enumerable and would like to avoid making two passes, consider a one-pass algorithm like Lasse V. Karlsen or Mark Byers describe. Both of these approaches use a temporary buffer to hold items while enumerating, which are yielded once the end of the collection is found.

How to skip last 2 records and get all other records with linq?

In this case: Take(8)

With Take and Skip you can get any range you want.

E.G:

var query = context.Test.OrderByDescending(t=>t.Id);
var allButTheLastTwoElements = query.Take(query.Count() - 2);

Safest way:

var query = context.Test.OrderByDescending(t=>t.Id).ToList();
var allButTheLastTwoElements = query.Take(Math.Max(0,query.Count() - 2));

Or you could just do it the other way around (depending on your requirements)

var query = context.Test.OrderByAscending(t=>t.Id).Skip(2);

How to get all elements except the n'th element in a List using Linq

Sure, using the overload of Where that takes an index parameter:

var allBut3 = chars.Where((c, i) => i != 2);  // use 2 since Where() uses 0-based indexing

Take all items except the last ones that satisfy condition?

There is no standard efficient LINQ solution. I would go with a custom extension "LINQ like" method like this:

public static class EnumerableExtensions
{
public static IEnumerable<T> SkipLastWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var skipBuffer = new List<T>();
foreach (var item in source)
{
if (predicate(item))
skipBuffer.Add(item);
else
{
if (skipBuffer.Count > 0)
{
foreach (var skipped in skipBuffer)
yield return skipped;
skipBuffer.Clear();
}
yield return item;
}
}
}
}

It requires additional space for buffering the longest item sequence satisfying the skip predicate while the LINQ Reverse method has to buffer the whole input sequence.

The usage will be:

var result = input.SkipLastWhile(e => !e.Any());

Extend a list using last element to match longer list in LINQ zip

Staying in the .Net/C# world:

You could use something like:

var almostInfinite = items.Concat(Enumerable.Repeat(items.Last(), Int32.MaxValue));

but it will not yield a really infinite sequence.

Writting your own Extend method isn't hard, either:

IEnumerable<T> Extend<T>(IEnumerable<T> source)
{
// error checking omitted
var e = source.GetEnumerator();
T last = default(T);
while(e.MoveNext())
yield return last = e.Current;
while(true)
yield return last;
}

You could also create another version of Zip. Take a look at morelinq's Zip, which handles source sequences of different sizes.

Sort List except one entry with LINQ, to be at the last

You can use either .Concat() to add the string that you desire at the end or use .OrderBy() to place your string at the end while leaving all others in the front and use .ThenBy() afterwards to sort the items. .ThenBy() will apply when you the previous orderings provide groups of items that are considered equal.

var list = new List<string> { "z", "u", "last", "b", "a" };
var result = list
.OrderBy(item => item == "last" ? 1 : 0)
.ThenBy(item => item);

Or in place sort

var list = new List<string> { "z", "u", "last", "b", "a" };
list.Sort((left, right) => left == "last" ? 1 : right == "last" ? -1 : string.Compare(left, right));

Using .Concat()

var list = new List<string> { "z", "u", "last", "b", "a" };
var result = list
.Where(item => item != "last")
.OrderBy(item => item)
.Concat(Enumerable.Repeat("last", 1));


Related Topics



Leave a reply



Submit