Skip to main content

arrow_cast/cast/
structs.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5use crate::cast::*;
6
7pub(crate) fn cast_struct_to_struct(
8    array: &StructArray,
9    from_fields: Fields,
10    to_fields: Fields,
11    cast_options: &CastOptions,
12) -> Result<ArrayRef, ArrowError> {
13    // Fast path: if field names are in the same order, we can just zip and cast
14    let fields_match_order = from_fields.len() == to_fields.len()
15        && from_fields
16            .iter()
17            .zip(to_fields.iter())
18            .all(|(f1, f2)| f1.name() == f2.name());
19
20    let fields = if fields_match_order {
21        // Fast path: cast columns in order if their names match
22        cast_struct_fields_in_order(array, to_fields.clone(), cast_options)?
23    } else {
24        let all_fields_match_by_name = to_fields.iter().all(|to_field| {
25            from_fields
26                .iter()
27                .any(|from_field| from_field.name() == to_field.name())
28        });
29
30        if all_fields_match_by_name {
31            // Slow path: match fields by name and reorder
32            cast_struct_fields_by_name(array, from_fields.clone(), to_fields.clone(), cast_options)?
33        } else {
34            // Fallback: cast field by field in order
35            cast_struct_fields_in_order(array, to_fields.clone(), cast_options)?
36        }
37    };
38
39    let array = StructArray::try_new(to_fields.clone(), fields, array.nulls().cloned())?;
40    Ok(Arc::new(array) as ArrayRef)
41}
42
43fn cast_struct_fields_by_name(
44    array: &StructArray,
45    from_fields: Fields,
46    to_fields: Fields,
47    cast_options: &CastOptions,
48) -> Result<Vec<ArrayRef>, ArrowError> {
49    to_fields
50        .iter()
51        .map(|to_field| {
52            let from_field_idx = from_fields
53                .iter()
54                .position(|from_field| from_field.name() == to_field.name())
55                .unwrap(); // safe because we checked above
56            let column = array.column(from_field_idx);
57            cast_with_options(column, to_field.data_type(), cast_options)
58        })
59        .collect::<Result<Vec<ArrayRef>, ArrowError>>()
60}
61
62fn cast_struct_fields_in_order(
63    array: &StructArray,
64    to_fields: Fields,
65    cast_options: &CastOptions,
66) -> Result<Vec<ArrayRef>, ArrowError> {
67    array
68        .columns()
69        .iter()
70        .zip(to_fields.iter())
71        .map(|(l, field)| cast_with_options(l, field.data_type(), cast_options))
72        .collect::<Result<Vec<ArrayRef>, ArrowError>>()
73}