@@ -80,6 +80,114 @@ const FunctionDoc list_value_length_doc{
8080 " Null values emit a null in the output." ),
8181 {" lists" }};
8282
83+ template <typename Type, typename IndexType>
84+ struct ListElementArray {
85+ static Status Exec (KernelContext* ctx, const ExecBatch& batch, Datum* out) {
86+ using ListArrayType = typename TypeTraits<Type>::ArrayType;
87+ using IndexScalarType = typename TypeTraits<IndexType>::ScalarType;
88+ const auto & index_scalar = batch[1 ].scalar_as <IndexScalarType>();
89+ if (ARROW_PREDICT_FALSE (!index_scalar.is_valid )) {
90+ return Status::Invalid (" Index must not be null" );
91+ }
92+ ListArrayType list_array (batch[0 ].array ());
93+ auto index = index_scalar.value ;
94+ if (ARROW_PREDICT_FALSE (index < 0 )) {
95+ return Status::Invalid (" Index " , index,
96+ " is out of bounds: should be greater than or equal to 0" );
97+ }
98+ std::unique_ptr<ArrayBuilder> builder;
99+ RETURN_NOT_OK (MakeBuilder (ctx->memory_pool (), list_array.value_type (), &builder));
100+ RETURN_NOT_OK (builder->Reserve (list_array.length ()));
101+ for (int i = 0 ; i < list_array.length (); ++i) {
102+ if (list_array.IsNull (i)) {
103+ RETURN_NOT_OK (builder->AppendNull ());
104+ continue ;
105+ }
106+ std::shared_ptr<arrow::Array> value_array = list_array.value_slice (i);
107+ auto len = value_array->length ();
108+ if (ARROW_PREDICT_FALSE (index >= static_cast <typename IndexType::c_type>(len))) {
109+ return Status::Invalid (" Index " , index, " is out of bounds: should be in [0, " ,
110+ len, " )" );
111+ }
112+ RETURN_NOT_OK (builder->AppendArraySlice (*value_array->data (), index, 1 ));
113+ }
114+ ARROW_ASSIGN_OR_RAISE (auto result, builder->Finish ());
115+ out->value = result->data ();
116+ return Status::OK ();
117+ }
118+ };
119+
120+ template <typename , typename IndexType>
121+ struct ListElementScalar {
122+ static Status Exec (KernelContext* /* ctx*/ , const ExecBatch& batch, Datum* out) {
123+ using IndexScalarType = typename TypeTraits<IndexType>::ScalarType;
124+ const auto & index_scalar = batch[1 ].scalar_as <IndexScalarType>();
125+ if (ARROW_PREDICT_FALSE (!index_scalar.is_valid )) {
126+ return Status::Invalid (" Index must not be null" );
127+ }
128+ const auto & list_scalar = batch[0 ].scalar_as <BaseListScalar>();
129+ if (ARROW_PREDICT_FALSE (!list_scalar.is_valid )) {
130+ out->value = MakeNullScalar (
131+ checked_cast<const BaseListType&>(*batch[0 ].type ()).value_type ());
132+ return Status::OK ();
133+ }
134+ auto list = list_scalar.value ;
135+ auto index = index_scalar.value ;
136+ auto len = list->length ();
137+ if (ARROW_PREDICT_FALSE (index < 0 ||
138+ index >= static_cast <typename IndexType::c_type>(len))) {
139+ return Status::Invalid (" Index " , index, " is out of bounds: should be in [0, " , len,
140+ " )" );
141+ }
142+ ARROW_ASSIGN_OR_RAISE (out->value , list->GetScalar (index));
143+ return Status::OK ();
144+ }
145+ };
146+
147+ template <typename InListType>
148+ void AddListElementArrayKernels (ScalarFunction* func) {
149+ for (const auto & index_type : IntTypes ()) {
150+ auto inputs = {InputType::Array (InListType::type_id), InputType::Scalar (index_type)};
151+ auto output = OutputType{ListValuesType};
152+ auto sig = KernelSignature::Make (std::move (inputs), std::move (output),
153+ /* is_varargs=*/ false );
154+ auto scalar_exec = GenerateInteger<ListElementArray, InListType>({index_type->id ()});
155+ ScalarKernel kernel{std::move (sig), std::move (scalar_exec)};
156+ kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE;
157+ kernel.mem_allocation = MemAllocation::NO_PREALLOCATE;
158+ DCHECK_OK (func->AddKernel (std::move (kernel)));
159+ }
160+ }
161+
162+ void AddListElementArrayKernels (ScalarFunction* func) {
163+ AddListElementArrayKernels<ListType>(func);
164+ AddListElementArrayKernels<LargeListType>(func);
165+ AddListElementArrayKernels<FixedSizeListType>(func);
166+ }
167+
168+ void AddListElementScalarKernels (ScalarFunction* func) {
169+ for (const auto list_type_id : {Type::LIST, Type::LARGE_LIST, Type::FIXED_SIZE_LIST}) {
170+ for (const auto & index_type : IntTypes ()) {
171+ auto inputs = {InputType::Scalar (list_type_id), InputType::Scalar (index_type)};
172+ auto output = OutputType{ListValuesType};
173+ auto sig = KernelSignature::Make (std::move (inputs), std::move (output),
174+ /* is_varargs=*/ false );
175+ auto scalar_exec = GenerateInteger<ListElementScalar, void >({index_type->id ()});
176+ ScalarKernel kernel{std::move (sig), std::move (scalar_exec)};
177+ kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE;
178+ kernel.mem_allocation = MemAllocation::NO_PREALLOCATE;
179+ DCHECK_OK (func->AddKernel (std::move (kernel)));
180+ }
181+ }
182+ }
183+
184+ const FunctionDoc list_element_doc (
185+ " Compute elements using of nested list values using an index" ,
186+ (" `lists` must have a list-like type.\n "
187+ " For each value in each list of `lists`, the element at `index`\n "
188+ " is emitted. Null values emit a null in the output." ),
189+ {" lists" , " index" });
190+
83191Result<ValueDescr> MakeStructResolve (KernelContext* ctx,
84192 const std::vector<ValueDescr>& descrs) {
85193 auto names = OptionsWrapper<MakeStructOptions>::Get (ctx).field_names ;
@@ -185,6 +293,12 @@ void RegisterScalarNested(FunctionRegistry* registry) {
185293 ListValueLength<LargeListType>));
186294 DCHECK_OK (registry->AddFunction (std::move (list_value_length)));
187295
296+ auto list_element = std::make_shared<ScalarFunction>(" list_element" , Arity::Binary (),
297+ &list_element_doc);
298+ AddListElementArrayKernels (list_element.get ());
299+ AddListElementScalarKernels (list_element.get ());
300+ DCHECK_OK (registry->AddFunction (std::move (list_element)));
301+
188302 static MakeStructOptions kDefaultMakeStructOptions ;
189303 auto make_struct_function = std::make_shared<ScalarFunction>(
190304 " make_struct" , Arity::VarArgs (), &make_struct_doc, &kDefaultMakeStructOptions );
0 commit comments