Arion
Arion

Reputation: 31239

Linq: How to get second last

So i have a List of strings that looks like this:

var ls=new List<string>()
    {
        "100",
        "101-102-1002",
        "105-153-1532-1532",
        "105-1854-45-198",
        "180-95-45-200"
    };

I want to get the second last of the the split string. So my output looks like this:

null,
102,
1532,
45,
45

I have a solution for it that looks like this:

ls.Select (l =>l.Split('-').Select ((s,i) =>new {s,i})
.OrderByDescending (x=>x.i).Skip(1).Take(1))

I think that this solution might be to complex for this simple task. So my question is: Do any of you have a simpler solution to this problem?

Upvotes: 43

Views: 34413

Answers (8)

Stu
Stu

Reputation: 89

I somehow managed across this page. None of Linq answers would work. When using FirstOrDefault you have to specify DefaultIfEmpty and populate it with a value. FirstOrDefault on it's own will break if at runtime the default is required. The accepted answer had DefaultIfEmpty but doesn't specify a value. It wouldn't work, the following example will.

var result = ls.DefaultIfEmpty(string.Empty).Reverse().Skip(1).Take(1).FirstOrDefault();

Upvotes: 4

Michael Freidgeim
Michael Freidgeim

Reputation: 28435

   I've created an extension based on Pavel Gatilov's answer above

public static TSource SecondLast<TSource>(this IEnumerable<TSource> source)
{
      //from http://stackoverflow.com/questions/8724179/linq-how-to-get-second-last
      return source.Reverse().Skip(1).Take(1).FirstOrDefault();
}

Upvotes: 5

magnifique
magnifique

Reputation: 1

In lambda syntax:

var ls = new List<string>() { "100", "101-102-1002", "105-153-1532-1532", "12-1235-785" };

var result = ls.Select(x => new { split = x.Split('-') }).Select(y => y.split.LastOrDefault(z => z != y.split.LastOrDefault()));

Upvotes: 0

Pavel Gatilov
Pavel Gatilov

Reputation: 7661

Reverse fits well here:

ls.SelectMany(l =>l.Split('-').Reverse().Skip(1).Take(1).DefaultIfEmpty())

I also use SelectMany to transform IEnumerable<IEnumerable<string>> to <IEnumerable<string>.

Upvotes: 51

corvuscorax
corvuscorax

Reputation: 5930

If you have

var ls = new List<string>( ... );

then

var result = ls.Reverse().Skip(1).Take(1);

should work.

Upvotes: 9

Jeff Ogata
Jeff Ogata

Reputation: 57783

var ls = new List<string>(){"100","101-102-1002","105-153-1532-1532","12-1235-785"}; 

var result = from l in ls
             let s = l.Split('-')
             select s.ElementAtOrDefault(s.Length - 2);

Upvotes: 7

shenhengbin
shenhengbin

Reputation: 4294

        var ls = new List<string>() { "100", "101-102-1002", "105-153-1532-1532", "12-1235-785" };
        var result = from p in ls
                     let arr = p.Split('-')
                     select arr.Length < 2 ? null : arr[arr.Length - 2];

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }



        Console.Read();

Upvotes: 15

Darin Dimitrov
Darin Dimitrov

Reputation: 1038720

var ls = new List<string> { "100", "101-102-1002", "105-153-1532-1532", "12-1235-785" };
var result = ls.Select(x =>
{
    var tokens = x.Split('-');
    if (tokens.Length < 2)
    {
        return null;
    }
    return tokens[tokens.Length - 2];
});

Upvotes: 2

Related Questions