Ecto query and Elixir case sentence

Hi,
I’m struggling to find out a solution.

  1. How to make the code below work without using macro as I don’t know it?
buyer_name = "jupiter"
item = "cash"  # or :cash
query = from(c in "balance_sheet", where: c.user_name == ^buyer_name, select: ^item)
  1. The code below works, but how to make it simpler?
item = "cash"
func = case item do
  "cash" -> {:ok, cash} = SomeModule.some_func(); fn ->  {:ok, cash} end
   _  -> "nothing"
  end
func.(). #  {:ok, cash}

Always thank you all.
Sooner or later, I will definately contribute to elixir forum.

I found the solution for the first. Use variable as key in Ecto's select_merge

Can you explain what you are trying to achieve with the 2nd code snippet? Is it complete, because when the case does not match it returns a string but you are still trying to execute it later as a function.

2 Likes

hi, hlx

my_map = %{a: 1, b: 2}
item_1 = "c"
item_2 = "d"
func = some_lambda
some_func(item_1, my_map) # {a: 1}
some_func(item_2, my_map) # {b: 2}

I need a function which returns a box inside which some value resides, rather than returning value itself.

My purpose is to retrieve an income statement(IS) account based on trading items of buyers and sellers. For example, when Alice buys some office supplies such as “pencils”(item_1) of which price is 10, then her “expense” account({expense: 1000}) of her IS(my_map) is to be retrieved and added the price {expense: 1010} before inserted back into Ecto.

Still not completely clear on the situation, maybe show your Ecto schema as well?

It does sound like you’d need some sort of mapping/lookup table between different line items such as a pencil or an eraser to prices/amount and how it’d affect the income statement (selling adds to revenue vs buying adds to expenses).

For example, "pencil" mapped to {:expense, 10}. Or even better, using a tuple like {"pencil", :expense, 10} {:expense, 10, "purchased a pencil"} or {:revenue, 20, "sold an eraser"}. Then you could have a function that updates the income statement map/struct.

income_statement = %{expense: 1000, revenue: 2000}
item_1 = {:expense, 10, "purchased a pencil"}
item_2 = {:revenue, 20, "sold an eraser"}

def update_income_statement({key, amount, _description}, income_statement) do
  update_in(income_statement, [key], &(&1 + amount)))
end

# results
update_income_statement(item_1, income_statement) => %{expense: 1010, revenue: 2000}
update_income_statement(item_2, income_statement) => %{expense: 1000, revenue: 2020}

While I wouldn’t suggest it but you could hardcode in those mapping if for some reason pencils are always purchased and erasers are always sold…

income_statement = %{expense: 1000, revenue: 2000}
item_1 = "pencil"
item_2 = "eraser"

# using a case statement
def some_func(item, income_statement) do
  case item do
    "pencil" -> %{income_statement | expense: income_statement.expense + 10}
    "product sale" -> %{income_statement | revenue: income_statement.revenue + 20}
  end
end

# or using pattern matching in function heads
def some_func("pencil", income_statement) do
  %{income_statement | expense: income_statement.expense + 10}
end

def some_func("product sale", income_statement) do
  %{income_statement | revenue: income_statement.revenue + 20}
end

# results
some_func(item_1, income_statement) => %{expense: 1010, revenue: 2000}
some_func(item_2, income_statement) => %{expense: 1000, revenue: 2020}
1 Like

Hi, codeanpeace
Definitely, your solution is the best and standard solution to the task.
My curiosity is whether there is some Elixir datatype similar to the Box in Rust or Haskell. For example,

buyer = "Alice"
item = "pencil" # there are numerous kinds of items
price = 10
alice_income_statement = %{office_supplies: 1000, depreciation: 1500, amortization: 2000} # there are dozens of income statement accounts

In my opinion, case do is not appropriate to this task. My intention is how to make the code below work, especially account_name in the map of the last line;

def accounting(item) do
    account_name = determine_account_name(item) |> update_database({price})
end

defp determine_account_for_item(item) do
    # some db search
    :office_supplies
end

defp  update_database({account_name, price}) do
    income_statement = Repo.get_by(IncomeStatement, user_name: ^buyer)
    new_account_balance = income_statement[account_name] + price # business logic in another module
   IncomeStatement.changeset(income_statement, %{account_name: ^new_account_balance}) |> Repo.insert
end

Why did you pin ^account_name on this line?

IncomeStatement.changeset(income_statement, %{account_name: ^new_account_balance}) |> Repo.insert
1 Like

That may be unnecessary.

In fact, I messed up the code. I wanna close this thread.

Thank you again, codeanpeace and hlx

1 Like

Ahh, I’m not familiar with the Box in Rust or Haskell.

If I’m finally understand what you’re asking, Map.fetch! (docs) and Map.put (docs) may be what you’re looking for to access and add a key value pair through a variable.

defp update_database({account_name, price}, buyer) do
    income_statement = Repo.get_by(IncomeStatement, user_name: buyer)
    new_account_balance = Map.fetch!(income_statement, account_name) + price
    IncomeStatement.changeset(
        income_statement, Map.put(%{}, account_name, new_account_balance)
    ) |> Repo.insert
end
1 Like