Skip to main content

ferray_strings/
split_join.rs

1// ferray-strings: Split and join operations (REQ-11)
2//
3// Implements split and join — elementwise on StringArray.
4
5use ferray_core::dimension::{Dimension, Ix1};
6use ferray_core::error::FerrayResult;
7
8use crate::string_array::{StringArray, StringArray1};
9
10/// Split each string element by the given separator.
11///
12/// Returns a 1-D array where each element is a `Vec<String>` containing
13/// the split parts. This matches NumPy's behavior where splitting produces
14/// a ragged result.
15///
16/// # Errors
17/// Returns an error if the internal array construction fails.
18pub fn split<D: Dimension>(a: &StringArray<D>, sep: &str) -> FerrayResult<Vec<Vec<String>>> {
19    let result: Vec<Vec<String>> = a
20        .iter()
21        .map(|s| s.split(sep).map(String::from).collect())
22        .collect();
23    Ok(result)
24}
25
26/// Join a collection of string vectors using the given separator.
27///
28/// Each element in the input is a `Vec<String>` which is joined into
29/// a single string. Returns a 1-D `StringArray`.
30///
31/// # Errors
32/// Returns an error if the internal array construction fails.
33pub fn join(sep: &str, items: &[Vec<String>]) -> FerrayResult<StringArray1> {
34    let data: Vec<String> = items.iter().map(|parts| parts.join(sep)).collect();
35    let dim = Ix1::new([data.len()]);
36    StringArray1::from_vec(dim, data)
37}
38
39/// Join each string element of a `StringArray` using the given separator.
40///
41/// This variant takes a `StringArray` and joins all elements into a single
42/// string. Returns a 1-D `StringArray` with one element.
43///
44/// # Errors
45/// Returns an error if the internal array construction fails.
46pub fn join_array<D: Dimension>(sep: &str, a: &StringArray<D>) -> FerrayResult<StringArray1> {
47    let joined: String = a
48        .iter()
49        .map(|s| s.as_str())
50        .collect::<Vec<&str>>()
51        .join(sep);
52    let dim = Ix1::new([1]);
53    StringArray1::from_vec(dim, vec![joined])
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::string_array::array;
60
61    #[test]
62    fn test_split() {
63        let a = array(&["a-b", "c-d"]).unwrap();
64        let result = split(&a, "-").unwrap();
65        assert_eq!(result.len(), 2);
66        assert_eq!(result[0], vec!["a", "b"]);
67        assert_eq!(result[1], vec!["c", "d"]);
68    }
69
70    #[test]
71    fn test_split_multiple_parts() {
72        let a = array(&["a-b-c"]).unwrap();
73        let result = split(&a, "-").unwrap();
74        assert_eq!(result[0], vec!["a", "b", "c"]);
75    }
76
77    #[test]
78    fn test_split_no_separator_found() {
79        let a = array(&["hello"]).unwrap();
80        let result = split(&a, "-").unwrap();
81        assert_eq!(result[0], vec!["hello"]);
82    }
83
84    #[test]
85    fn test_join() {
86        let items = vec![
87            vec!["a".to_string(), "b".to_string()],
88            vec!["c".to_string(), "d".to_string()],
89        ];
90        let result = join("-", &items).unwrap();
91        assert_eq!(result.as_slice(), &["a-b", "c-d"]);
92    }
93
94    #[test]
95    fn test_join_array() {
96        let a = array(&["hello", "world"]).unwrap();
97        let result = join_array(" ", &a).unwrap();
98        assert_eq!(result.as_slice(), &["hello world"]);
99    }
100
101    #[test]
102    fn test_split_ac4() {
103        // AC-4: strings::split(&["a-b", "c-d"], "-") returns [vec!["a","b"], vec!["c","d"]]
104        let a = array(&["a-b", "c-d"]).unwrap();
105        let result = split(&a, "-").unwrap();
106        assert_eq!(
107            result,
108            vec![
109                vec!["a".to_string(), "b".to_string()],
110                vec!["c".to_string(), "d".to_string()],
111            ]
112        );
113    }
114}