Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions lib/helper/utils/map.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
defmodule Helper.Utils.Map do
@moduledoc """
unitil functions
"""
def map_key_stringify(%{__struct__: _} = map) when is_map(map) do
map = Map.from_struct(map)
map |> Enum.reduce(%{}, fn {key, val}, acc -> Map.put(acc, to_string(key), val) end)
end

def map_key_stringify(map) when is_map(map) do
map |> Enum.reduce(%{}, fn {key, val}, acc -> Map.put(acc, to_string(key), val) end)
end

@doc """
see https://stackoverflow.com/a/61559842/4050784
adjust it for map keys from atom to string
"""
def keys_to_atoms(json) when is_map(json) do
Map.new(json, &reduce_keys_to_atoms/1)
end

def keys_to_atoms(string) when is_binary(string), do: string

defp reduce_keys_to_atoms({key, val}) when is_map(val),
# do: {String.to_existing_atom(key), keys_to_atoms(val)}
do: {String.to_atom(key), keys_to_atoms(val)}

defp reduce_keys_to_atoms({key, val}) when is_list(val),
do: {String.to_atom(key), Enum.map(val, &keys_to_atoms(&1))}

defp reduce_keys_to_atoms({key, val}), do: {String.to_atom(key), val}

@doc """
see https://stackoverflow.com/a/61559842/4050784
adjust it for map keys from atom to string
"""
@spec keys_to_strings(map) :: map
def keys_to_strings(json) when is_map(json) do
Map.new(json, &reduce_keys_to_strings/1)
end

defp reduce_keys_to_strings({key, val}) when is_map(val),
do: {Atom.to_string(key), keys_to_strings(val)}

defp reduce_keys_to_strings({key, val}) when is_list(val),
do: {Atom.to_string(key), Enum.map(val, &keys_to_strings(&1))}

defp reduce_keys_to_strings({key, val}), do: {Atom.to_string(key), val}

@doc """
Recursivly camelize the map keys
usage: convert factory attrs to used for simu Graphql parmas
"""
def camelize_map_key(map, v_trans \\ :ignore) do
map_list =
Enum.map(map, fn {k, v} ->
v =
cond do
is_datetime?(v) ->
DateTime.to_iso8601(v)

is_map(v) ->
camelize_map_key(safe_map(v))

is_binary(v) ->
handle_camelize_value_trans(v, v_trans)

true ->
v
end

map_to_camel({k, v})
end)

Enum.into(map_list, %{})
end

defp handle_camelize_value_trans(v, :ignore), do: v
defp handle_camelize_value_trans(v, :downcase), do: String.downcase(v)
defp handle_camelize_value_trans(v, :upcase), do: String.upcase(v)

defp safe_map(%{__struct__: _} = map), do: Map.from_struct(map)
defp safe_map(map), do: map

defp map_to_camel({k, v}), do: {Recase.to_camel(to_string(k)), v}

@spec snake_map_key(map) :: map
def snake_map_key(map) do
map_list =
Enum.map(map, fn {k, v} ->
v =
cond do
is_datetime?(v) ->
DateTime.to_iso8601(v)

is_map(v) ->
snake_map_key(safe_map(v))

true ->
v
end

{Recase.to_snake(to_string(k)), v}
end)

Enum.into(map_list, %{})
end

defp is_datetime?(%DateTime{}), do: true
defp is_datetime?(_), do: false

def map_atom_value(attrs, :string) do
results =
Enum.map(attrs, fn {k, v} ->
cond do
v == true or v == false ->
{k, v}

is_atom(v) ->
{k, v |> to_string() |> String.downcase()}

true ->
{k, v}
end
end)

results |> Enum.into(%{})
end

def deep_merge(left, right), do: Map.merge(left, right, &deep_resolve/3)

# Key exists in both maps, and both values are maps as well.
# These can be merged recursively.
# defp deep_resolve(_key, left = %{},right = %{}) do
defp deep_resolve(_key, %{} = left, %{} = right), do: deep_merge(left, right)

# Key exists in both maps, but at least one of the values is
# NOT a map. We fall back to standard merge behavior, preferring
# the value on the right.
defp deep_resolve(_key, _left, right), do: right
end
30 changes: 30 additions & 0 deletions lib/helper/utils/string.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule Helper.Utils.String do
@moduledoc """
string utils
"""

def stringfy(v) when is_binary(v), do: v
def stringfy(v) when is_integer(v), do: to_string(v)
def stringfy(v) when is_atom(v), do: to_string(v)
def stringfy(v), do: v

# see https://stackoverflow.com/a/49558074/4050784
@spec str_occurence(String.t(), String.t()) :: Integer.t()
def str_occurence(string, substr) when is_binary(string) and is_binary(substr) do
len = string |> String.split(substr) |> length()
len - 1
end

def str_occurence(_, _), do: "must be strings"

@doc """
["a", "b", "c", "c"] => %{"a" => 1, "b" => 1, "c" => 2}
"""
def count_words(words) when is_list(words) do
Enum.reduce(words, %{}, &update_word_count/2)
end

defp update_word_count(word, acc) do
Map.update(acc, to_string(word), 1, &(&1 + 1))
end
end
181 changes: 16 additions & 165 deletions lib/helper/utils.ex → lib/helper/utils/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ defmodule Helper.Utils do

import Helper.Validator.Guards, only: [g_none_empty_str: 1]

alias Helper.Cache
alias Helper.{Cache, Utils}

# Map utils
defdelegate map_key_stringify(map), to: Utils.Map
defdelegate keys_to_atoms(map), to: Utils.Map
defdelegate keys_to_strings(map), to: Utils.Map
defdelegate camelize_map_key(map), to: Utils.Map
defdelegate camelize_map_key(map, opt), to: Utils.Map
defdelegate snake_map_key(map), to: Utils.Map
defdelegate deep_merge(left, right), to: Utils.Map
defdelegate map_atom_value(attrs, opt), to: Utils.Map

# String Utils
defdelegate stringfy(str), to: Utils.String
defdelegate count_words(str), to: Utils.String
defdelegate str_occurence(string, substr), to: Utils.String

def get_config(section, key, app \\ :groupher_server)

Expand Down Expand Up @@ -87,125 +102,9 @@ defmodule Helper.Utils do
|> Absinthe.Resolution.put_result({:error, message: err_msg, code: ecode()})
end

def map_key_stringify(%{__struct__: _} = map) when is_map(map) do
map = Map.from_struct(map)
map |> Enum.reduce(%{}, fn {key, val}, acc -> Map.put(acc, to_string(key), val) end)
end

def map_key_stringify(map) when is_map(map) do
map |> Enum.reduce(%{}, fn {key, val}, acc -> Map.put(acc, to_string(key), val) end)
end

@doc """
see https://stackoverflow.com/a/61559842/4050784
adjust it for map keys from atom to string
"""
def keys_to_atoms(json) when is_map(json) do
Map.new(json, &reduce_keys_to_atoms/1)
end

def keys_to_atoms(string) when is_binary(string), do: string

def reduce_keys_to_atoms({key, val}) when is_map(val),
# do: {String.to_existing_atom(key), keys_to_atoms(val)}
do: {String.to_atom(key), keys_to_atoms(val)}

def reduce_keys_to_atoms({key, val}) when is_list(val),
do: {String.to_atom(key), Enum.map(val, &keys_to_atoms(&1))}

def reduce_keys_to_atoms({key, val}), do: {String.to_atom(key), val}

@doc """
see https://stackoverflow.com/a/61559842/4050784
adjust it for map keys from atom to string
"""
@spec keys_to_strings(map) :: map
def keys_to_strings(json) when is_map(json) do
Map.new(json, &reduce_keys_to_strings/1)
end

defp reduce_keys_to_strings({key, val}) when is_map(val),
do: {Atom.to_string(key), keys_to_strings(val)}

defp reduce_keys_to_strings({key, val}) when is_list(val),
do: {Atom.to_string(key), Enum.map(val, &keys_to_strings(&1))}

defp reduce_keys_to_strings({key, val}), do: {Atom.to_string(key), val}

@doc """
Recursivly camelize the map keys
usage: convert factory attrs to used for simu Graphql parmas
"""
def camelize_map_key(map, v_trans \\ :ignore) do
map_list =
Enum.map(map, fn {k, v} ->
v =
cond do
is_datetime?(v) ->
DateTime.to_iso8601(v)

is_map(v) ->
camelize_map_key(safe_map(v))

is_binary(v) ->
handle_camelize_value_trans(v, v_trans)

true ->
v
end

map_to_camel({k, v})
end)

Enum.into(map_list, %{})
end

defp handle_camelize_value_trans(v, :ignore), do: v
defp handle_camelize_value_trans(v, :downcase), do: String.downcase(v)
defp handle_camelize_value_trans(v, :upcase), do: String.upcase(v)

defp safe_map(%{__struct__: _} = map), do: Map.from_struct(map)
defp safe_map(map), do: map

defp map_to_camel({k, v}), do: {Recase.to_camel(to_string(k)), v}

@spec snake_map_key(map) :: map
def snake_map_key(map) do
map_list =
Enum.map(map, fn {k, v} ->
v =
cond do
is_datetime?(v) ->
DateTime.to_iso8601(v)

is_map(v) ->
snake_map_key(safe_map(v))

true ->
v
end

{Recase.to_snake(to_string(k)), v}
end)

Enum.into(map_list, %{})
end

def is_datetime?(%DateTime{}), do: true
def is_datetime?(_), do: false

def deep_merge(left, right) do
Map.merge(left, right, &deep_resolve/3)
end

def integerfy(id) when is_binary(id), do: String.to_integer(id)
def integerfy(id), do: id

def stringfy(v) when is_binary(v), do: v
def stringfy(v) when is_integer(v), do: to_string(v)
def stringfy(v) when is_atom(v), do: to_string(v)
def stringfy(v), do: v

# TODO: enhance, doc
def repeat(times, [x]) when is_integer(x), do: to_string(for _ <- 1..times, do: x)
def repeat(times, x), do: for(_ <- 1..times, do: x)
Expand All @@ -220,58 +119,10 @@ defmodule Helper.Utils do
end)
end

def map_atom_value(attrs, :string) do
results =
Enum.map(attrs, fn {k, v} ->
cond do
v == true or v == false ->
{k, v}

is_atom(v) ->
{k, v |> to_string() |> String.downcase()}

true ->
{k, v}
end
end)

results |> Enum.into(%{})
end

def empty_pagi_data do
%{entries: [], total_count: 0, page_size: 0, total_pages: 1, page_number: 1}
end

# Key exists in both maps, and both values are maps as well.
# These can be merged recursively.
# defp deep_resolve(_key, left = %{},right = %{}) do
defp deep_resolve(_key, %{} = left, %{} = right), do: deep_merge(left, right)

# Key exists in both maps, but at least one of the values is
# NOT a map. We fall back to standard merge behavior, preferring
# the value on the right.
defp deep_resolve(_key, _left, right), do: right

@doc """
["a", "b", "c", "c"] => %{"a" => 1, "b" => 1, "c" => 2}
"""
def count_words(words) when is_list(words) do
Enum.reduce(words, %{}, &update_word_count/2)
end

defp update_word_count(word, acc) do
Map.update(acc, to_string(word), 1, &(&1 + 1))
end

# see https://stackoverflow.com/a/49558074/4050784
@spec str_occurence(String.t(), String.t()) :: Integer.t()
def str_occurence(string, substr) when is_binary(string) and is_binary(substr) do
len = string |> String.split(substr) |> length()
len - 1
end

def str_occurence(_, _), do: "must be strings"

@spec large_than(String.t() | Integer.t(), Integer.t()) :: true | false
def large_than(value, target) when is_binary(value) and is_integer(target) do
String.length(value) >= target
Expand Down