Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions extensions/functions_list.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,32 @@ scalar_functions:
value: func<any1 -> boolean?>
nullability: DECLARED_OUTPUT
return: boolean?

- name: "subscript_operator"
description: >-
Returns the element at the specified index from the list, corresponding
to the common array subscript operator [].

Index is 1-based (i.e., the first element is at index 1). Out-of-bounds
Comment on lines +137 to +139
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe #917 is proposing 0-based index 😆 I believe I commented whether we should offer an option to do either zero-based index or one-based index... So, we'd better be consistent.

access (index <= 0 or index > list length) returns null.
impls:
- args:
- name: input
value: list<any1>
- name: index
value: i64
nullability: DECLARED_OUTPUT
Comment thread
benbellick marked this conversation as resolved.
return: any1?

- name: "index_of"
description: >-
Returns the 1-based index of the first occurrence of the specified value
in the list, or null if the value is not found.
impls:
- args:
- name: input
value: list<any1>
- name: value
value: any1
nullability: DECLARED_OUTPUT
return: i64?
27 changes: 27 additions & 0 deletions tests/cases/list/index_of.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
### SUBSTRAIT_SCALAR_TEST: v1.0
### SUBSTRAIT_INCLUDE: '/extensions/functions_list.yaml'

# basic: Find element in list (1-based index)
index_of([10, 20, 30]::list<i32>, 10::i32) = 1::i64?
index_of([10, 20, 30]::list<i32>, 20::i32) = 2::i64?
index_of([10, 20, 30]::list<i32>, 30::i32) = 3::i64?
index_of(['a', 'b', 'c']::list<string>, 'b'::string) = 2::i64?

# not_found: Element not in list returns null
index_of([10, 20, 30]::list<i32>, 5::i32) = null::i64?
index_of([10, 20, 30]::list<i32>, 100::i32) = null::i64?

# first_match: Returns index of first occurrence
index_of([10, 20, 20, 30]::list<i32>, 20::i32) = 2::i64?

# empty_list: Empty list returns null
index_of([]::list<i32>, 1::i32) = null::i64?

# null_list: Null list returns null
index_of(null::list?<i32>, 1::i32) = null::i64?

# null_value: Searching for null returns null
index_of([10, 20, 30]::list<i32>, null::i32?) = null::i64?

# find_null_element: Finding null in list with nulls
index_of([1, null, 3]::list<i32?>, null::i32?) = 2::i64?
31 changes: 31 additions & 0 deletions tests/cases/list/subscript_operator.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
### SUBSTRAIT_SCALAR_TEST: v1.0
### SUBSTRAIT_INCLUDE: '/extensions/functions_list.yaml'

# basic: Basic positive index (1-based)
subscript_operator([10, 20, 30]::list<i32>, 1::i64) = 10::i32?
subscript_operator([10, 20, 30]::list<i32>, 2::i64) = 20::i32?
subscript_operator([10, 20, 30]::list<i32>, 3::i64) = 30::i32?
subscript_operator(['a', 'b', 'c']::list<string>, 2::i64) = 'b'::string?

# out_of_bounds_positive: Positive index exceeds list length
subscript_operator([10, 20, 30]::list<i32>, 5::i64) = null::i32?
subscript_operator([10, 20, 30]::list<i32>, 100::i64) = null::i32?

# zero_out_of_bounds: Zero index is out of bounds
subscript_operator([10, 20, 30]::list<i32>, 0::i64) = null::i32?

# negative_out_of_bounds: Negative indices are out of bounds
subscript_operator([10, 20, 30]::list<i32>, -1::i64) = null::i32?
subscript_operator([10, 20, 30]::list<i32>, -3::i64) = null::i32?

# null_list: Null list returns null
subscript_operator(null::list?<i32>, 1::i64) = null::i32?

# null_index: Null index returns null
subscript_operator([10, 20, 30]::list<i32>, null::i64?) = null::i32?

# null_element: Retrieving a null element returns null
subscript_operator([1, null, 3]::list<i32?>, 2::i64) = null::i32?

# empty_list: Out of bounds on empty list
subscript_operator([]::list<i32>, 1::i64) = null::i32?
Loading