defmodule RList.ActiveSupport do
@moduledoc """
Summarized all of List functions in Rails.ActiveSupport.
If a function with the same name already exists in Elixir, that is not implemented.
Defines all of here functions when `use RList.ActiveSupport`.
"""
@spec __using__(any) :: list
defmacro __using__(_opts) do
RUtils.define_all_functions!(__MODULE__)
end
alias RList.Support
# https://www.rubydoc.info/gems/activesupport/Array
# [:as_json, :compact_blank!, :deep_dup, :excluding, :extract!, :extract_options!, :fifth, :forty_two, :fourth, :from, :in_groups, :in_groups_of, :including, :inquiry, :second, :second_to_last, :split, :sum, :third, :third_to_last, :to, :to_default_s, :to_formatted_s, :to_param, :to_query, :to_s, :to_sentence, :to_xml]
# |> RUtils.required_functions([List, RList.Ruby, REnum])
# × as_json
# × deep_dup
# ✔ fifth
# ✔ forty_two
# ✔ fourth
# ✔ from
# ✔ in_groups
# ✔ in_groups_of
# inquiry
# ✔ second
# ✔ second_to_last
# ✔ third
# ✔ third_to_last
# ✔ to
# ✔ to_default_s
# × to_formatted_s
# to_param
# to_query
# ✔ to_sentence
# to_xml
@doc """
Returns the tail of the list from position.
## Examples
iex> ~w[a b c d]
iex> |> RList.from(0)
["a", "b", "c", "d"]
iex> ~w[a b c d]
iex> |> RList.from(2)
["c", "d"]
iex> ~w[a b c d]
iex> |> RList.from(10)
[]
iex> ~w[]
iex> |> RList.from(0)
[]
iex> ~w[a b c d]
iex> |> RList.from(-2)
["c", "d"]
iex> ~w[a b c d]
iex> |> RList.from(-10)
[]
"""
@spec from(list(), integer()) :: list()
def from(list, position) do
list
|> Enum.slice(position..Enum.count(list))
end
@doc """
Returns the beginning of the list up to position.
## Examples
iex> ~w[a b c d]
iex> |> RList.to(0)
["a"]
iex> ~w[a b c d]
iex> |> RList.to(2)
["a", "b", "c"]
iex> ~w[a b c d]
iex> |> RList.to(10)
["a", "b", "c", "d"]
iex> ~w[]
iex> |> RList.to(0)
[]
iex> ~w[a b c d]
iex> |> RList.to(-2)
["a", "b", "c"]
iex> ~w[a b c d]
iex> |> RList.to(-10)
[]
"""
@spec to(list(), integer()) :: list()
def to(list, position) do
list
|> Enum.slice(0..position)
end
@doc """
Equal to `Enum.at(list, 1)`.
## Examples
iex> ~w[a b c d]
iex> |> RList.second()
"b"
"""
@spec second(list()) :: any()
def second(list) do
Enum.at(list, 1)
end
@doc """
Equal to `Enum.at(list, 2)`.
## Examples
iex> ~w[a b c d]
iex> |> RList.third()
"c"
"""
@spec third(list()) :: any()
def third(list) do
Enum.at(list, 2)
end
@doc """
Equal to `Enum.at(list, 3)`.
## Examples
iex> ~w[a b c d]
iex> |> RList.fourth()
"d"
"""
@spec fourth(list()) :: any()
def fourth(list) do
Enum.at(list, 3)
end
@doc """
Equal to `Enum.at(list, 4)`.
## Examples
iex> ~w[a b c d e]
iex> |> RList.fifth()
"e"
"""
@spec fifth(list()) :: any()
def fifth(list) do
Enum.at(list, 4)
end
@doc """
Equal to `Enum.at(list, 41)`. Also known as accessing "the reddit".
## Examples
iex> 1..42
iex> |> RList.forty_two()
42
"""
@spec forty_two(list()) :: any()
def forty_two(list) do
Enum.at(list, 41)
end
@doc """
Equal to `Enum.at(list, -2)`.
## Examples
iex> ~w[a b c d e]
iex> |> RList.second_to_last()
"d"
"""
@spec second_to_last(list()) :: any()
def second_to_last(list) do
Enum.at(list, -2)
end
@doc """
Equal to `Enum.at(list, -3)`.
## Examples
iex> ~w[a b c d e]
iex> |> RList.third_to_last()
"c"
"""
@spec third_to_last(list()) :: any()
def third_to_last(list) do
Enum.at(list, -3)
end
@doc """
Converts the list to a comma-separated sentence where the last element is joined by the connector word.
You can pass the following options to change the default behavior. If you pass an option key that doesn't exist in the list below, it will raise an
** Options **
* `:words_connector` - The sign or word used to join all but the last
element in lists with three or more elements (default: ", ").
* `:last_word_connector` - The sign or word used to join the last element
in lists with three or more elements (default: ", and ").
* `:two_words_connector` - The sign or word used to join the elements
in lists with two elements (default: " and ").
## Examples
iex> ["one", "two"]
iex> |> RList.to_sentence()
"one and two"
iex> ["one", "two", "three"]
iex> |> RList.to_sentence()
"one, two, and three"
iex> ["one", "two"]
iex> |> RList.to_sentence(two_words_connector: "-")
"one-two"
iex> ["one", "two", "three"]
iex> |> RList.to_sentence(words_connector: " or ", last_word_connector: " or at least ")
"one or two or at least three"
iex> ["one", "two", "three"]
iex> |> RList.to_sentence()
"one, two, and three"
"""
@spec to_sentence(list(), list(keyword()) | nil) :: String.t()
def to_sentence(list, opts \\ []) do
words_connector = Keyword.get(opts, :words_connector) || ", "
two_words_connector = Keyword.get(opts, :two_words_connector) || " and "
last_word_connector = Keyword.get(opts, :last_word_connector) || ", and "
case Enum.count(list) do
0 -> ""
1 -> "#{Enum.at(list, 0)}"
2 -> "#{Enum.at(list, 0)}#{two_words_connector}#{Enum.at(list, 1)}"
_ -> "#{to(list, -2) |> Enum.join(words_connector)}#{last_word_connector}#{List.last(list)}"
end
end
@doc """
Splits or iterates over the list in number of groups, padding any remaining slots with fill_with unless it is false.
## Examples
iex> ~w[1 2 3 4 5 6 7 8 9 10]
iex> |> RList.in_groups(3)
[
["1", "2", "3", "4"],
["5", "6", "7", nil],
["8", "9", "10", nil]
]
iex> ~w[1 2 3 4 5 6 7 8 9 10]
iex> |> RList.in_groups(3, " ")
[
["1", "2", "3", "4"],
["5", "6", "7", " "],
["8", "9", "10", " "]
]
iex> ~w[1 2 3 4 5 6 7]
iex> |> RList.in_groups(3, false)
[
["1", "2", "3"],
["4", "5"],
["6", "7"]
]
"""
@spec in_groups(list(), non_neg_integer(), any() | nil) :: list()
def in_groups(list, number, fill_with \\ nil) do
division = div(Enum.count(list), number)
modulo = rem(Enum.count(list), number)
range = 0..(number - 1)
length_list =
range
|> Enum.map(&(division + if(modulo > 0 && modulo > &1, do: 1, else: 0)))
|> IO.inspect()
range
|> Enum.reduce([], fn index, acc ->
length = length_list |> Enum.at(index)
group =
Enum.slice(
list,
length_list
|> Enum.take(index)
|> Enum.sum(),
length
)
if fill_with != false && modulo > 0 && length == division do
acc ++ [group ++ [fill_with]]
else
acc ++ [group]
end
end)
end
@doc """
Splits or iterates over the list in groups of size number, padding any remaining slots with fill_with unless it is +false+.
## Examples
iex> ~w[1 2 3 4 5 6 7 8 9 10]
iex> |> RList.in_groups_of(3)
[
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"],
["10", nil, nil]
]
iex> ~w[1 2 3 4 5]
iex> |> RList.in_groups_of(2, " ")
[
["1", "2"],
["3", "4"],
["5", " "]
]
iex> ~w[1 2 3 4 5]
iex> |> RList.in_groups_of(2, false)
[
["1", "2"],
["3", "4"],
["5"]
]
"""
@spec in_groups_of(list(), non_neg_integer(), any() | nil) :: list()
def in_groups_of(list, number, fill_with \\ nil) do
if(fill_with == false) do
list
else
padding = rem(number - rem(Enum.count(list), number), number)
list ++ Support.new(fill_with, padding)
end
|> REnum.each_slice(number)
|> Enum.to_list()
end
defdelegate to_default_s(list), to: Kernel, as: :inspect
end