Random Snippets
Mix.install([
:req,
{:jason, "~> 1.0"},
{:csv, "~> 3.0"}
])
Sorting list of maps with custom sort
[%{name: "asdf9"}, %{name: "zzzzzzzz1"}, %{name: "2"}, %{name: "55"}, %{name: "3iiijij4"}]
|> Enum.sort_by(
fn lm_rule ->
lm_rule.name
|> String.replace(~r/\D/, "")
|> case do
"" -> ""
name -> String.to_integer(name)
end
end,
:asc
)
Random subset
defmodule Test do
# list of elements, n, random subset
def random_subset(list, n) when is_list(list) and length(list) >= n do
{new_list, _} =
Enum.reduce(1..n, {[], list}, fn _item, acc ->
{new_list, old_list} = acc
length = length(old_list)
random_index = :rand.uniform(length) - 1
{item, old_list} = List.pop_at(old_list, random_index)
{[item | new_list], old_list}
end)
new_list
end
def random_subset(list, _n) do
list
end
end
IO.inspect(Test.random_subset([], 2))
IO.inspect(Test.random_subset([1], 3))
IO.inspect(Test.random_subset([1, 2, 3], 2))
IO.inspect(Test.random_subset([1, 2, 3, 4, 5, 6], -1))
IO.inspect(Test.random_subset(["asdf", 24, "iaojsdf", false], 3))
IO.inspect(Test.random_subset(123, 4))
Parsing JSON events from Cassandra query
defmodule JsonQuery do
def read_json_files(files) do
Enum.reduce(files, [], fn file, acc ->
case read_json_file(file) do
{:ok, json} ->
acc ++ json
{:error, _} ->
acc
end
end)
end
defp read_json_file(file) do
with {:ok, body} <- File.read(file),
{:ok, json} <- Jason.decode(body) do
{:ok, json}
end
end
defp get_value(map, key) do
case map[key] do
nil -> {:error, nil}
value -> {:ok, value}
end
end
defp decode_json(value) do
case value do
nil -> {:error, nil}
%{} -> {:error, nil}
value -> Jason.decode(value)
end
end
defp parse_message(message) do
case message do
nil ->
{:error, nil}
[_connector_id, transaction_id, event_type, payload] ->
{:ok, transaction_id, event_type, payload}
_ ->
{:error, nil}
end
end
defp parse_meter_values(meter_values, key) do
Enum.reduce(meter_values, [], fn meter_value, acc ->
case meter_value[key] do
nil -> acc
value -> [value | acc]
end
end)
|> MapSet.new()
|> then(fn set ->
case MapSet.size(set) do
1 ->
{:ok, MapSet.to_list(set) |> List.first()}
_ ->
IO.puts("Warning: multiple timestamps found in meter values")
{:error, nil}
end
end)
end
defp compare_datetimes(dt1, dt2) do
with {:ok, dt1, 0} <- DateTime.from_iso8601(dt1),
{:ok, dt2, 0} <- DateTime.from_iso8601(dt2) do
{:ok, DateTime.diff(dt1, dt2)}
else
_ -> {:error, nil}
end
end
def parse_data(rows) do
Enum.reduce(rows, [], fn row, acc ->
with {:ok, log_timestamp} <- get_value(row, "timestamp"),
{:ok, data} <- decode_json(row["data"]),
{:ok, message} <- decode_json(data["message"]),
{:ok, transaction_id, event_type, payload} <- parse_message(message),
{:ok, meter_values} <- get_value(payload, "meterValue"),
{:ok, event_timestamp} <- parse_meter_values(meter_values, "timestamp"),
{:ok, difference_in_seconds} <- compare_datetimes(log_timestamp, event_timestamp) do
[
%{
transaction_id: transaction_id,
log_timestamp: log_timestamp,
event_type: event_type,
event_timestamp: event_timestamp,
difference_in_seconds: difference_in_seconds
}
| acc
]
else
_ -> acc
end
end)
end
defp seconds_to_largest_unit(seconds) do
minutes = 60
hours = 60 * minutes
days = 24 * hours
weeks = 7 * days
months = 4 * weeks
years = 12 * months
case seconds do
seconds when seconds > years -> "#{(seconds / years) |> Float.round(2)} years"
seconds when seconds > months -> "#{(seconds / months) |> Float.round(2)} months"
seconds when seconds > weeks -> "#{(seconds / weeks) |> Float.round(2)} weeks"
seconds when seconds > days -> "#{(seconds / days) |> Float.round(2)} days"
seconds when seconds > hours -> "#{(seconds / hours) |> Float.round(2)} hours"
seconds when seconds > minutes -> "#{(seconds / minutes) |> Float.round(2)} minutes"
seconds -> "#{seconds} seconds"
end
end
def filter_values_with_different_timestamps(rows) do
Enum.filter(rows, fn row ->
row[:difference_in_seconds] > 1
end)
|> Enum.map(fn row ->
Map.put(row, :difference, seconds_to_largest_unit(row[:difference_in_seconds]))
|> Map.delete(:difference_in_seconds)
end)
end
def chunk_and_print_by_field(rows, key) do
rows
|> Enum.chunk_by(fn row -> row[key] end)
|> Enum.map(fn chunk ->
first = chunk |> List.first() |> Map.get(key)
IO.puts("\t#{Enum.count(chunk)} for #{key}: #{first}")
end)
end
end
dir = "/Users/noah/Misc/cassandra-query"
# Uncomment if you use this in the command line via:
# `elixir query.exs ./feb-raw-events.json ./mar-raw-events.json`
# json = System.argv()
json =
[
"#{dir}/feb-raw-events.json",
"#{dir}/mar-raw-events.json"
]
|> JsonQuery.read_json_files()
|> tap(fn rows ->
IO.puts("Found #{Enum.count(rows)} raw events")
JsonQuery.chunk_and_print_by_field(rows, "timestamp_month")
end)
|> JsonQuery.parse_data()
|> tap(fn rows ->
IO.puts("Found #{Enum.count(rows)} meter value events")
JsonQuery.chunk_and_print_by_field(rows, :timestamp_month)
end)
|> JsonQuery.filter_values_with_different_timestamps()
|> tap(fn rows ->
IO.puts("Found #{Enum.count(rows)} meter value events with different timestamps")
JsonQuery.chunk_and_print_by_field(rows, :timestamp_month)
end)
|> Jason.encode!()
File.write!("./result.json", json)
CSV Encode
defmodule StreamTest do
require Logger
def build(rows) do
encoded_rows =
try do
rows
|> CSV.encode()
rescue
e ->
Logger.error("Error while encoding rows: #{inspect(e)}")
[]
end
{:ok, encoded_rows}
end
end
{:ok, encoded_rows} = StreamTest.build([["asdf"], [",,,,,"], ["{:id, 1}"], ["opqr"], ["stuv"]])
encoded_rows |> IO.inspect(label: "Before to_list")
encoded_rows |> Enum.to_list() |> IO.inspect(label: "After to_list")
Quick Return Check
defmodule Testy do
def return_ok() do
:ok
end
def return_value() do
:ok = return_ok()
end
end
Testy.return_value() |> IO.inspect(label: "return")
Naive DateTime
%{
no_tz_info: ~N[2015-01-13 12:00:00.000],
capital_z_utc: ~N[2015-01-13 12:00:00.0Z],
offset_utc_minus_8: ~N[2015-01-13T12:00:00-08:00]
}
obj = nil
obj && obj.thing