I’m used to Rust where iterators have a ton of handy functions. What I’d like to do is equivalent to the following Rust code:
my_iterator.iter()
.skip(n)
.map(|x| my_fun(x))
.collect()
In more concrete terms
fn my_fun(x: i32) -> i32 { x * 10 }
let my_iterator = vec![1,2,3,4,5];
my_iterator.iter()
.skip(2)
.map(|x| my_fun(x))
.collect::<Vec<i32>>()
// [1,2,30,40,50]
Is there an equivalent in Elixir? Or would I need to do something like
my_list = [1,2,3,4,5]
map_or_not(my_list)
def map_or_not([1 | [ 2 | t ]]) do
[1 | [2 |map_or_not(t)]]
end
def map_or_not(list) do
list |> Enum.map(fn x -> x * 10 end)
end
That’s not the behavior I’m seeing from skip. When I run that I get [30, 40, 50]
. It appears to behave like Enum.drop/2 or Stream.drop/2.
As for how to get the behavior you want, you could use Enum.reduce/3 or Stream.scan/3, using an acc to count down elements not to touch, then performing the mapping function when the acc is 0.
Or even easier might be Enumerable.map_reduce/3:
{result, _acc} =
Enum.map_reduce([1, 2, 3, 4, 5], 2, fn
item, 0 ->
{item * 10, 0}
item, acc ->
{item, acc - 1}
end)
result
# => [1, 2, 30, 40, 50]
2 Likes
Sorry, rust code should have used iter_mut() without a collect() call on a mutable vec. Good suggestion for the countdown accumulator. Thanks.
You should be able to write exactly what you mean:
s = 1..5
new_stream =
Stream.concat(
Stream.take(s, 2),
Stream.drop(s, 2) |> Stream.map(& &1*10)
)
Enum.to_list(new_stream) # => [1, 2, 30, 40, 50]
One caveat: this will traverse the first two elements of the stream twice. That’s no problem for most Elixir data structures, but will cause strange behavior when streaming from a stateful resource like the result of File.stream!
.
2 Likes
I think you can make it simply
list = 1..5 # or list = [1,2,3,4,5]
skip_by_val = fn
x, y when x <= y -> x
x, _y -> 10*x
end
list |> Stream.map(&skip_by_val.(&1, 2)) |> Enum.to_list()
or
skip_by_idx = fn
{x, idx_x}, idx when idx_x <= idx -> x
{x, _idx_x}, _idx -> 10*x
end
list |> Stream.with_index(1) |> Stream.map(&skip_by_idx.(&1, 2)) |> Enum.to_list()
1 Like
Thanks for all the help! Stream.concat is a pretty obvious. literal translation of what I was trying to do, so thanks for that tip. And simply pattern matching the index/position for applying the mapping function is probably the most obvious way to do what I want. Thanks again.