2

I'm in the process of learning Rust, but I could not find an answer to this question.

In PHP, there's the array_column method and it works this way:

given an array of arrays (this would be a a Vector of vectors in Rust):

$records = [
    [1,2,3],
    [1,2,3],
    [1,2,3],
    [1,2,3]
];

if I want to get an array containing all the first elements (a "column") of the inner arrays I can do:

$column = array_column($records, 0);

This way, for example, I get [1,1,1,1]. If I change that 0 with 1, I get [2,2,2,2] and so on.

Since there's no array_column equivalent in Rust (that is: I could not find it), what could be the best way to implement a similar behavior with a vector of vectors?

2
  • I assume PHP doesn't do any magic with memory, because subarrays are probably placed in differents parts of memory. So just make a new vector with capacity of records's size, then iterate each element in outer vector, and add in your result a subvector[k] element. You should also check if every subvector has size greater than k Commented May 7, 2020 at 14:12
  • 1
    @AlexLarionov: thanks for your comment! Basically, your solution is my solution but I thought I was missing something big when I found you can do something like this: let zipped: Vec<_> = a.iter().zip(b.iter()).collect(); (this only works with 2 vectors and it's not enough for me). I don't know what PHP does at a low level, but the implementation of array_column is really fast Commented May 7, 2020 at 14:27

2 Answers 2

2

I decided to play with iterators, as you tried in the comments.

This version works with any clonable values (numbers included). We iterate over subvectors, and for each we call a get method, which either yields an element of the vector Some(&e) or None if we ask out of bounds.

and_then then accepts a value from get, and if it was None, then None is returned, otherwise, if it's Some(&e) then Some(e.clone()) is returned, i.e. we clone the value (because we only have the reference to the value from get, we can't store it, we have to copy the value).

collect then works with Iter<Option<T>>, and it conveniently turns it in Option<Vec<T>>, i.e. it returns None if some Nones were in the iterator (which means some arrays didn't have big enough size), or returns Some(Vec<T>), if everything is fine.

fn main() {
    let array = vec![
        vec![1, 2, 3, 4],
        vec![1, 2, 3, 4, 5],
        vec![1, 2, 3, 4],
        vec![1, 2, 3, 4],
    ];
    let ac = array_column(&array, 0);
    println!("{:?}", ac); // Some([1, 1, 1, 1])

    let ac = array_column(&array, 3);
    println!("{:?}", ac); // Some([4, 4, 4, 4])

    let ac = array_column(&array, 4); // None
    println!("{:?}", ac);
}

fn array_column<T: Clone>(array: &Vec<Vec<T>>, column: usize) -> Option<Vec<T>> {
    array.iter()
        .map( |subvec| subvec.get(column).and_then(|e| Some(e.clone())) )
        .collect()
}
Sign up to request clarification or add additional context in comments.

Comments

2

Alex version is good, but you can generalize it using references too, so there will be no need for the item to be Clone:

fn array_column<'a, T>(array: &'a Vec<Vec<T>>, column: usize) -> Option<Vec<&'a T>> {
    array.iter()
        .map( |subvec| subvec.get(column) )
        .collect()
}

Playground

2 Comments

Exactly, but as a side note, you cannot drop the source array while a result of the function exists somewhere. Might be inconvenient to use then
@AlexLarionov, well, you have the compiler check that for you so you can always explicitly clone later when needed also.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.