Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indexing arrays from the end #10104

Open
jeskew opened this issue Mar 13, 2023 · 1 comment
Open

Indexing arrays from the end #10104

jeskew opened this issue Mar 13, 2023 · 1 comment
Labels
design approved The team has reviewed and signed off on this design enhancement New feature or request good first issue Good for newcomers proposal

Comments

@jeskew
Copy link
Contributor

jeskew commented Mar 13, 2023

Array elements in ARM can only be accessed by supplying the zero-based index as a property accessor. When a user wants to access an element by its position relative to the end of the array, there are two options:

  1. Use an index of length(<array>) - <offset from end>, or
  2. Use the last function.

These approaches cannot be used in all cases. The first construct dereferences the array in two separate locations and is therefore only compatible with named values. The second construct can only access a specific index and is somewhat misleadingly named; it is the equivalent of C#'s .LastOrDefault() method rather than C#'s .Last() method (in that it will return null rather than raise an error when called on an empty collection).

For accessing arbitrary offsets from the end of anonymous values, a separate approach is needed.

Proposal

Bicep should add a new operator to access an array element by its position relative to the end of the array. Following C#'s example, I suggest that we use a circumflex (^) character that follows the opening square bracket and precedes the index expression. For example:

param anArray array

var second_to_last_element = anArray[^2]

This operator should be combinable with the safe dereference operator:

param anArray array

var second_to_last_element_if_exists = anArray[?^2]

In ARM, this operator would be represented by two fuctions: indexFromEnd (corresponding to [^<index>]) and tryIndexFromEnd (corresponding to [?^<index>]). Both functions would take exactly two parameters:

  1. The array being indexed into, and
  2. The index from the end of the array.

For the second parameter, only natural numbers would be accepted. This value would be translated into a normal, zero-based array index by subtracting the provided value from the length of the collection. I.e., given an array with five elements, indexFromEnd(<array>, 2) would be equivalent to <array>[length(<array>) - 2] (or simply <array>[3]).

If the resolved zero-based index is negative, indexFromEnd would raise an error, whereas tryIndexFromEnd would return null. tryIndexFromEnd should also return null if the first parameter is not an array (to match the behavior of tryGet(<not an array>, 0)), whereas this would raise an error in indexFromEnd.

Alternatives considered

Support negative indicies in ARM and Bicep

While an attractively simple proposal, supporting negative indices directly in ARM (i.e., <array>[-1] to get the last element of an array) could make some expressions that would previously have raised an error instead return an unexpected value. indexOf and lastIndexOf use -1 as a sentinel value meaning that the sought element was not found in the provided collection; it would be odd if <array>[indexOf(<arrayToSearch>, <element>)] simply returned the last element of the array should <element> not be found in <arrayToSearch>.

@jeskew jeskew added enhancement New feature or request proposal design approved The team has reviewed and signed off on this design labels Mar 13, 2023
@ghost ghost added the Needs: Triage 🔍 label Mar 13, 2023
@jeskew jeskew added this to the Committed Backlog milestone Mar 13, 2023
@jeskew jeskew added the intermediate language Related to the intermediate language label Mar 15, 2023
@jeskew jeskew added good first issue Good for newcomers and removed intermediate language Related to the intermediate language labels Jun 26, 2024
@jeskew
Copy link
Contributor Author

jeskew commented Jun 26, 2024

The ARM functions to back this have been implemented as indexFromEnd and tryIndexFromEnd and have rolled out everywhere:

var myArray = [3, 4, 5]
var penultimate = myArray[^2] // == 4
var outOfBounds = myArray[?^10] // == null

would compile to:

{
  ...
  "variables": {
    "myArray": [1, 2, 3],
    "penultimate": "[indexFromEnd(variables('myArray'), 2)]",
    "outOfBounds": "[tryIndexFromEnd(variables('myArray'), 10)]"
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design approved The team has reviewed and signed off on this design enhancement New feature or request good first issue Good for newcomers proposal
Projects
Status: Todo
Development

No branches or pull requests

1 participant