minhajuddin

minhajuddin

Is there a simpler way to generate ids

I am using the code below to generate room ids:

reservations = [%{rooms: 1},
                %{rooms: 2},
                %{rooms: 3}]

{reservations_with_room_numbers, _} =
  Enum.reduce(reservations, {[], 0}, fn reservation, {acc, offset} ->
    upper_limit = offset+reservation.rooms - 1
  room_numbers = Enum.to_list(offset..upper_limit)
  reservation_with_room_numbers = Map.put(reservation, :room_numbers, room_numbers)
  {[reservation_with_room_numbers|acc], upper_limit + 1}
end)

IO.inspect Enum.reverse(reservations_with_room_numbers)
# =>
# [
# %{room_numbers: [0], rooms: 1}
# %{room_numbers: [1, 2], rooms: 2},
# %{room_numbers: [3, 4, 5], rooms: 3},
# ]

The code above seems too complicated for such a simple task, Is there a simpler way to do the same?

System.unique_integer [:monotonic, :positive] gives out unique integers starting at 1, However there is no way to reset it after the generation is done, so that it starts from 1 for the next call.

Marked As Solved

josevalim

josevalim

Creator of Elixir

I believe your initial solution looks good, I would just use map_reduce instead of reduce to clean things up:

reservations = [%{rooms: 1},
                %{rooms: 2},
                %{rooms: 3}]

{reservations_with_room_numbers, _} =
  Enum.map_reduce(reservations, 0, fn reservation, offset ->
    upper_limit = offset+reservation.rooms - 1
    room_numbers = Enum.to_list(offset..upper_limit)
    reservation_with_room_numbers = Map.put(reservation, :room_numbers, room_numbers)
    {reservation_with_room_numbers, upper_limit + 1}
  end)

Also Liked

JEG2

JEG2

Author of Designing Elixir Systems with OTP

Or, with a generator:

defmodule Generator do
  def new(start) do
    Agent.start_link(fn -> start end)
  end

  def next(generator) do
    Agent.get_and_update(generator, fn value -> {value, value + 1} end)
  end
end

defmodule RoomBuilder do
  def build([{:rooms, n} | rooms], built, generator) do
    room_numbers =
      Stream.repeatedly(fn -> Generator.next(generator) end)
      |> Enum.take(n)
    build(rooms, [room_numbers | built], generator)
  end
  def build([ ], built, _generator), do: Enum.reverse(built)
end

{:ok, generator} = Generator.new(0)
reservations = [rooms: 1, rooms: 2, rooms: 3]
RoomBuilder.build(reservations, [ ], generator)
|> IO.inspect
benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

One thing that isn’t clear is if we want a pure or impure solution. The original post had both. They’re wildly different solutions and people need to be aware of that.

michalmuskala

michalmuskala

I believe this shows another use case for a function like Stream.split/2 - similar to Enum.split/2, but one that would leave the “rest” part as a stream. This would allow for the most natural way to solve this:

ids = Stream.itrerate(1, &(&1 + 1))
Enum.map_reduce(reservations, ids, fn reservation, ids ->
  {room_numbers, rest} = Stream.split(ids, reservation.rooms)
  {Map.put(reservation, :room_numbers, room_numbers), rest}
end) |> elem(0)
sasajuric

sasajuric

Author of Elixir In Action

It requires more code, but I tend to wrap such reductions in separate modules. Not sure about the wider context of your problem, so let’s say that multiple reservations make a singe “booking”. Then, I can have the booking module:

defmodule Booking do
  def new(), do: %{offset: 0, reservations: []}

  def reserve(booking, rooms) do
    reservation =
      %{rooms: rooms, room_numbers: Enum.map(1..rooms, &(&1 + booking.offset))}

    %{booking |
      reservations: [reservation | booking.reservations],
      offset: booking.offset + rooms
    }
  end

  def reservations(booking), do: Enum.reverse(booking.reservations)
end

That’s more LOC, but on the upside, the client code can IMO become more readable:

[%{rooms: 1}, %{rooms: 2}, %{rooms: 3}]
|> Enum.reduce(Booking.new(), &Booking.reserve(&2, &1.rooms))
|> Booking.reservations()

# [%{room_numbers: [1], rooms: 1}, %{room_numbers: [2, 3], rooms: 2},
# %{room_numbers: [4, 5, 6], rooms: 3}]

If the module functionality is small, and it’s used in only one place, I just “inline” these funs (in this case new/0, reserve/2, reservations/1), i.e. implement them as private in the same module where I use them.

josevalim

josevalim

Creator of Elixir

:+1: for Stream.split/2.

Where Next?

Popular in Questions Top

JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement