0

Is there a simple way to get the next to last item from an array? For example, I might have the following string arrays:

var myArr1 = new string[]{"S1", "S2", "S3"};
var myArr2 = new string[]{"S1", "S2", "S3", "S4"};

I need to write a generic routine which would return "S2.S3" for myArr1 and "S3.S4" for myArr2. Getting then last item is easy enough with Last() but there doesn't appear to be a Last(-1) option. Is there anything similar to this? If not then what would be the easiest and most elegant way to do this?

user9393635
  • 1,369
  • 4
  • 13
  • 32
  • 6
    `myArr[myArr.length - 2] +"." + myArr[myArr.length - 1]` - this should do! – Am_I_Helpful May 14 '18 at 16:59
  • 1
    $"{myArr1[myArr1.Length - 2]}.{myArr1.Last()}" – Sebastian Hofmann May 14 '18 at 17:00
  • 1
    Here is a big question about this: https://stackoverflow.com/q/3453274/5311735 (concentrates on using LINQ though). – Evk May 14 '18 at 17:02
  • 1
    You'll need to do some range checking on it... – Nyerguds May 14 '18 at 17:11
  • 1
    Possible duplicate of https://stackoverflow.com/questions/22857137/how-to-find-second-last-element-from-a-list – Arpit Gupta May 14 '18 at 17:13
  • Are you constrained to working with arrays? Arrays are fairly shonky to deal with and, unless you're implementing collection classes yourself, it's usually better to pick a collection class that (may) more directly support the operations you're looking for. – Damien_The_Unbeliever May 14 '18 at 17:13
  • Not sure about efficiency but one other way is - myArr1.Reverse().ToArray()[1]; – Arpit Gupta May 14 '18 at 17:18
  • Possible duplicate of [How to find second last element from a List?](https://stackoverflow.com/questions/22857137/how-to-find-second-last-element-from-a-list) – BJ Myers May 14 '18 at 17:23
  • Why are you asking this? Why not just access the last two items by index? Or use the (inefficient) `myArr1.Skip(myArr1.Length -2)` to skip all items except the last two? – Panagiotis Kanavos May 14 '18 at 17:28
  • 3
    I have created a nuget package with some extra LINQ-like operator methods for .NET collections (not usable with entity framework or the likes) that has what you need - https://github.com/lassevk/linqy - in this case it would be `string.Join(".", myArr1.Last(2))`, you can add it inside Visual Studio using the nuget package manager. – Lasse V. Karlsen May 14 '18 at 17:28
  • And what if your array contains one or less elements? – chaosifier May 14 '18 at 17:37

5 Answers5

2

You can get the second last element of an array using this code...

myArr1[myArr1.Length - 2]

myArr2[myArr2.Length - 2] 

Output

S2    
S3

Online Demo: http://rextester.com/AERTN64718  

Updated...

myArr1[myArr1.Length - 2] + "." + myArr1[myArr1.Length - 1]

myArr2[myArr2.Length - 2] + "." + myArr2[myArr2.Length - 1]

or

myArr1[myArr1.Length - 2] + "." + myArr1.Last()

myArr2[myArr2.Length - 2] + "." + myArr2.Last()

Output

S2.S3
S3.S4

Online Demo: http://rextester.com/DJU86580

DxTx
  • 3,049
  • 3
  • 23
  • 34
1

This is based of off @Ehsan's answer (which was in VB, but I translated it to C#)

string LastTwoStrings(string[] array)
{
    return (array[array.Length-2] + "." + array[array.Length-1]);
}

However, this WILL throw an exception if the array is smaller than 2.

Daxtron2
  • 1,239
  • 1
  • 11
  • 19
1

Using System.Linq you could do:

String.Join(".", arr.Skip(arr.Length - 2));
chaosifier
  • 2,666
  • 25
  • 39
1

Depending on your C# version (8.0+) you may also have access to an unary expression that counts the index in reverse from the current collection. E.g. just like indexing from the front [0] to get 1st entry, you can get the the last entry by counting from the back [^1].

This is to be read like "length-1" of the current collection.

private readonly string[] _entries = new[]
{
   "Hello",
   "How's life?",
   "Likewise",
   "Bye"
};

public string GetLastEntry()
{
   return _entries[^1]; // Returns "Bye" and is the same as "_entries[_entries.Length-1]"
}

So for your particular example, you could just use string.Join() for the string-based use case, but let's try the reverse index. With those, we could output the last n entries like here:

private readonly string[] _entries = { "S1", "S2", "S3", "S4" };

public string GetTailingEntries(int depth)
{
    if (depth < 0 || depth > _entries.Length)
    {
        // Catch this however you require
        throw new IndexOutOfRangeException();
    }

    string output = _entries[^depth--];
    while (depth > 0)
    {
        output += $".{_entries[^depth--]}";
    }

    return output;
}

Essentially we take any depth, let's take '2' like in your question. It would initialize the output with the furthest entry from the end ([^2]) which is "S3" and decrement the depth, putting the next entry at [^1]. In the loop we append the values to the output until we reached our desired depth.

GetTailingEntries(2) // "S3" -> "S3.S4"
GetTailingEntries(4) // "S1" -> "S1.S2" -> "S1.S2.S3" -> "S1.S2.S3.S4"
0

With LINQ

This need is so basic and intuitive that I would consider writing my own Last(int) extension method-- exactly the one you'd imagine-- and it might look like this:

public static class ExtensionMethods
{
    public static IEnumerable<T> Last<T>(this IEnumerable<T> This, int count)
    {
        return This.Reverse().Take(count).Reverse();
    }
}

You can then get what you need with:

Console.WriteLine
(
    string.Join(".", myArr1.Last(2))
);

Which outputs:

S2.S3

Using array (more efficient)

On the other hand, if you're looking for efficiency, you should just work with the array and the known indices, which will be more efficient that using IEnumerable (which will have to scan).

public static IEnumerable<T> Last<T>(this T[] This, int count)
{
    var i = This.Length;
    if (count > i) count = i;
    while (count-- > 0) yield return This[--i];
}

...does exactly the same thing, but only for arrays.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • `Take` only takes as long as there are elements, so it's 100% safe, but that last one could give out of range exceptions... – Nyerguds May 14 '18 at 19:54
  • 1
    @Nyerguds Thanks, I added a boundary condition based on your feedback. – John Wu May 14 '18 at 23:05