hissssst / pathex Goto Github PK
View Code? Open in Web Editor NEWFastest tool to access data in Elixir
License: BSD 2-Clause "Simplified" License
Fastest tool to access data in Elixir
License: BSD 2-Clause "Simplified" License
Great Library!
using a variable inside a tuple key fails, also I think the error message about the pin operator is now invalid.
iex > val = "xxx"
iex > path :x / {:foo, val}
** (CompileError) cannot use variable val as map key inside a pattern. Map keys in patterns can only be literals (such as atoms, strings, tuples, and the like) or an existing variable matched with the pin operator (such as ^some_var)
but wrapping the tuple in a variable works just fine
iex > key = {:foo, val}
{:foo, "xxx"}
iex > path :x / key
#Function<43.65746770/2 in :erl_eval.expr/5>
I want to create a dynamic path.
data = [{:a, []}, {:b, [{:c, []}]}]
assert {:ok, {:c, []}} = view(data, path 1 / 1 / 0)
This is good, but what if I don't know [1, 1, 0] at compile time. I couldn't see anything documented about passing a list as a path, so I tried the following:
def make_path(elems) do
elems
|> Enum.map(fn elem -> path elem end)
|> Enum.reduce(fn p1, p2 -> p1 ~> p2 end)
end
assert {:ok, {:c, []}} = view(data, make_path([1, 1, 0]))
# fails with :error
Any idea what I am missing here?
Thanks.
Matt
Is it possible to add a function to delete a key?
p = path "hey" / "you"
%{"hey" => %{"x" => 2}} == delete %{"hey" => %{"you" => 1, "x" => 2}}, p
p = path "hey" / 1
%{"hey" => [0, 2]} == delete %{"hey" => [0, 1, 2]}, p
== Compilation error in file lib/pathex/accessibility.ex ==
** (ArgumentError) cannot pipe value into function.()
|> case do
{:ok, value} -> value
:error -> throw(:path_not_found)
end, the :|> operator can only take two arguments
(elixir 1.14.0-rc.0) lib/macro.ex:323: Macro.pipe/3
(elixir 1.14.0-rc.0) expanding macro: Kernel.|>/2
lib/pathex/accessibility.ex:144: Pathex.Accessibility.from_list/2
(elixir 1.14.0-rc.0) expanding macro: Kernel.|>/2
lib/pathex/accessibility.ex:144: Pathex.Accessibility.from_list/2
expanding macro: Pathex.path/2
lib/pathex/accessibility.ex:144: Pathex.Accessibility.from_list/2
great library, this could be elixir issue
Hi there, apologies if I missed this in the documentation, but is it possible to define the collection type when creating a path via from_list?
e.g. I want to create something similar to path :alpha / (0 :: :list)
, but dynamically.
I attempted to do something like from_list([:alpha, 0 :: :list])
, however, I got an error saying that ::
was not a defined function.
The use case for this is that I want to use the above path to force_set
and create if it doesn't exist, but I want it to create a list rather than a map. From testing it seems that defining the collection type correctly results in a list being created for the 0
path.
Any assistance would be much appreciated!
Thanks!
I'd love for delete(struct,path) to have an option to always return struct, even if path wasn't found in struct.
I'm not sure if it's a new call, or an optional third param to delete() or pop().
When using (some) functions which rely on the magic :delete_me
atom, Dialyzer complains about a pattern that can never match the type (this error is for the most generic case where no types can be inferred at the place of use):
The pattern
'delete_me' can never match the type
'error' | {'ok', [any()] | tuple() | map()}
Affected functions (for me) are delete/2
, delete!/2
, and pop!/1
. pop/1
Isn't affected, and I'm not sure why.
Paste this module into any project which has pathex dependency installed, and run Dialyzer.
defmodule PathexTest do
use Pathex
def pop_key_from_my_map(%{} = my_map, key) do
Pathex.delete(my_map, path(key)) # The pattern 'delete_me' can never match the type 'error' | {'ok',map()}
Pathex.delete!(my_map, path(key)) # The pattern 'delete_me' can never match the type 'error' | {'ok',map()}
Pathex.pop!(my_map, path(key)) # The pattern 'delete_me' can never match the type 'error' | {'ok',map()}
Pathex.pop(my_map, path(key)) # No warning for some reason?
end
end
For me this is reproducable on a mix new
project with two dependencies, but also causes a warning in VSCode even without the dialyxir
dependency:
[
{:pathex, "~> 2.4.2"},
{:dialyxir, "~> 1.0", only: [:dev], runtime: false}
]
My environment:
> elixir --version
Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Elixir 1.14.1 (compiled with Erlang/OTP 25)
iex> path(y :: :list, :json)
** (FunctionClauseError) no function clause matching in Pathex.Builder.ForceUpdater.reduce_into/2
The following arguments were given to Pathex.Builder.ForceUpdater.reduce_into/2:
# 1
[]
# 2
{{:|>, [context: Pathex.Builder.ForceUpdater, imports: [{2, Kernel}]],
[
{{:., [], [{:function, [], Elixir}]}, [], []},
{:case, [],
[
[
do: [
{:->, [],
[
[ok: {:value, [], Pathex.Builder.ForceUpdater}],
{:value, [], Pathex.Builder.ForceUpdater}
]},
{:->, [],
[
[:error],
{:throw,
[context: Pathex.Builder.ForceUpdater, imports: [{1, Kernel}]],
[:path_not_found]}
]}
]
]
]}
]}, {:default, [], Elixir}}
Attempted function clauses (showing 1 out of 1):
defp reduce_into([path_item | _] = path_items, {acc_code, acc_items})
(pathex 2.5.1) lib/pathex/builder/updaters/force_updater.ex:26: Pathex.Builder.ForceUpdater.reduce_into/2
(elixir 1.14.3) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
(pathex 2.5.1) lib/pathex/builder/updaters/force_updater.ex:21: Pathex.Builder.ForceUpdater.build/1
(pathex 2.5.1) lib/pathex/builder.ex:47: anonymous fn/2 in Pathex.Builder.build/2
(elixir 1.14.3) lib/enum.ex:1662: anonymous fn/3 in Enum.map/2
(stdlib 4.2) maps.erl:411: :maps.fold_1/3
(elixir 1.14.3) lib/enum.ex:2480: Enum.map/2
(pathex 2.5.1) lib/pathex/builder.ex:46: Pathex.Builder.build/2
iex> s = [
%{
children: [
%{
children: [],
id: "fcfb1066-6e16-4a84-81e0-7746f12b7e76",
tag: "test1",
type: "section"
},
%{
id: "035ca737-2cc2-488b-83f5-fe7cc2becc5e",
parent_id: "a9963499-f6a4-4a32-bafa-aa22a9b059bc"
},
%{
children: [
%{
other: [
%{
id: "035ca737-2cc2-488b-83f5-fe7cc2becc5e",
parent_id: "a9963499-f6a4-4a32-bafa-aa22a9b059bc"
}
],
tag: "test4"
}
],
id: "8a9f0126-33a0-4c03-8c4a-5507f0562c8a",
index: 2,
parent: "layout",
parent_id: "a9963499-f6a4-4a32-bafa-aa22a9b059bc",
tag: "test2",
type: "section"
}
],
parent: "dragLocation",
parent_id: "dragLocation",
tag: "test3"
}
]
iex> Pathex.view s, tag_lens ~> matching("test2")
{:ok, "test2"}
iex> Pathex.exists? s, tag_lens ~> matching("test2")
** (CaseClauseError) no case clause matching: true
(pathex 2.5.0) lib/pathex/lenses/some.ex:186: Pathex.Lenses.Some.map_view/2
iex:6: (file)
iex:6: (file)
iex:6: (file)
iex:9: (file)
Let's say I have the following data:
data = %{
"key" => "value",
"list" => [
%{
"a" => 1,
"b" => 2
},
%{
"a" => 3,
"b" => 4
}
]
}
and my goal is to delete all "a"
keys from all maps in the inner list. So, I create a path for them:
all_a = path("list") ~> Pathex.Lenses.all() ~> path("a")
Pathex.get(data, all_a)
# result:
[1, 3]
But when I use delete!
it just wipes all items in the list:
Pathex.delete!(data, all_a)
# result:
%{"key" => "value", "list" => []}
Then I tried to not use the lens, but still do concatenation:
first_a_concat = path("list") ~> path(0) ~> path("a")
Pathex.get(data, first_a_concat)
# result:
1
Pathex.delete!(data, first_a_concat)
# result:
%{"key" => "value", "list" => [%{"a" => 3, "b" => 4}]} # it removes first item from the list!
When I use path without concatenation it works as it should:
first_a_path = path("list" / 0 / "a")
Pathex.get(data, first_a_path)
# result:
1
Pathex.delete!(data, first_a_path)
# result:
%{"key" => "value", "list" => [%{"b" => 2}, %{"a" => 3, "b" => 4}]}
Then I tried to check what will happen with some deeeep data:
deep_data = %{
"list" => [
%{
"list" => [
%{
"list" => [
%{
"a" => 1,
"b" => 2
}
]
}
]
}
]
}
deep_dive_path =
path("list")
~> Pathex.Lenses.all()
~> path("list")
~> Pathex.Lenses.all()
~> path("list")
~> Pathex.Lenses.all()
~> path("a")
Pathex.get(deep_data, deep_dive_path)
# result:
[[[1]]] # btw, shouldn't it be a flat list here?
Pathex.delete!(deep_data, deep_dive_path)
# result:
** (CaseClauseError) no case clause matching: :delete_me
(pathex 2.4.0) lib/pathex/lenses/all.ex:111: anonymous fn/3 in Pathex.Lenses.All.all/0
(elixir 1.14.0) lib/enum.ex:4751: Enumerable.List.reduce/3
(elixir 1.14.0) lib/enum.ex:2514: Enum.reduce_while/3
(pathex 2.4.0) lib/pathex/lenses/all.ex:110: anonymous fn/2 in Pathex.Lenses.All.all/0
(stdlib 3.17.2) erl_eval.erl:672: :erl_eval.do_apply/5
(stdlib 3.17.2) erl_eval.erl:270: :erl_eval.expr/5
(pathex 2.4.0) lib/pathex/lenses/all.ex:111: anonymous fn/3 in Pathex.Lenses.All.all/0
(elixir 1.14.0) lib/enum.ex:4751: Enumerable.List.reduce/3
So, it looks like delete!/2
has some real problems with lenses and concatenation. =)
As @ffloyd mentioned in #20, I see some weird behavior with all/0 lens:
import Pathex
data = [
%{ "a" => 1 },
%{ "a" => 2 }
]
all_a = Pathex.Lenses.all() ~> path("a")
Pathex.get(data, all_a) |> IO.inspect(label: "get")
Pathex.delete!(data, all_a) |> IO.inspect(label: "delete!")
And as result get/2 works, but delete/2 fails:
** (Pathex.Error)
Couldn't find element
Path: all() ~> path("a")
Structure: [%{"a" => 1}, %{"a" => 2}]
(stdlib 3.17.2) erl_eval.erl:685: :erl_eval.do_apply/6
(stdlib 3.17.2) erl_eval.erl:893: :erl_eval.expr_list/6
(stdlib 3.17.2) erl_eval.erl:408: :erl_eval.expr/5
(elixir 1.14.0) lib/module/parallel_checker.ex:100: Module.ParallelChecker.verify/1
Pathex is super neat, but the ergonomics are hampered by having to import 2-3 different modules for even relatively simple uses. This example on the forum, for instance: https://elixirforum.com/t/find-a-specific-string-in-an-unstructured-and-nested-list/53918/10?u=zachallaun
Iโd propose that use Pathex
should be updated to import a much larger set of primitives, including those from combinator, lenses, etc.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.