-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Access.filter/1 #6634
Conversation
This looks great, and extremely useful. |
I checked on PR #6364 and saw that it restructures documentation, so I haven't touched the main documentation, only the documentation specific to the functions I'm adding. |
lib/elixir/lib/access.ex
Outdated
@@ -700,4 +700,158 @@ defmodule Access do | |||
defp get_and_update_at([], _index, _next, updates) do | |||
{nil, :lists.reverse(updates)} | |||
end | |||
|
|||
@doc ~S""" | |||
Returns a function that accesses all elements of a list that match the provided predicate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole docs should be two spaces.
lib/elixir/lib/access.ex
Outdated
|
||
## Examples | ||
|
||
iex> list = [%{name: "john", salary: 10}, %{name: "mary", salary: 20}, %{name: "francine", salary: 30}] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably do it with a list of two to simplify examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up going with a list of 3 items to show order preservation, and specifically with filter
to show how order is preserved on both sides. I don't mind changing it though, if you feel strongly about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could also write a non-example test to confirm that, and use the simpler example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could also write a non-example test to confirm that, and use the simpler example?
Perfect.
lib/elixir/lib/access.ex
Outdated
iex> get_in(%{}, [Access.filter(fn a -> a == 10 end)]) | ||
** (RuntimeError) Access.filter/1 expected a list, got: %{} | ||
""" | ||
def filter(func) when is_function(func, 1) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to not check the arity because that leads to a better error later on the invocation site.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'll change now.
Thank you @zachdaniel! I like |
@josevalim I agree. I really use |
Although on large lists, it would perform much worse. Either way, I'm on board to remove |
lib/elixir/lib/access.ex
Outdated
raise "Access.filter/1 expected a list, got: #{inspect data}" | ||
end | ||
|
||
defp get_and_update_where([head | rest], func, next, updates, gets) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's rename where
to filter
here and below then please? thanks!
Sounds good. I'll make the following changes when I get home tonight:
|
@josevalim The above items are complete. |
@test_list [1, 2, 3, 4, 5, 6] | ||
|
||
test "filters in get_in" do | ||
assert(get_in(@test_list, [Access.filter(&(&1 > 3))]) == [4, 5, 6]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please remove the parens around assert
on this test and the ones below?
|
||
test "chains with other access functions" do | ||
mixed_map_and_list = %{foo: Enum.map(@test_list, &(%{value: &1}))} | ||
assert(get_in(mixed_map_and_list, [:foo, Access.filter(&(&1 <= 3)), :value]) == []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is a bit confusing. Maybe it would be better to filter based on is_tuple/1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure exactly what you mean. None of the elements of the list are tuples. get_and_update_in
returns a tuples of the get value and update value though. I think using the @test_list module attribute is just less clear,and maybe it would be clearer with an explicit test list in each test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your filter is comparing a map with <= 3
. If you want to reject everything, it would be better to have a filter based on the data structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, is_tuple
was wrong, is_map
is probably better. You do &(&1 <= 3)
where &1
is a map which doesn't really make sense. Is %{value: 1} <= 3
the comparison you wanted to make? It would be better if you changed it to a more natural comparison.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, sorry it was supposed to be &1.value <= 3
I'm pretty sure the test should be failing with that mistake though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't fail because all terms are comparable, even across different types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it's just wrong all around. Shouldn't be asserting an empty list. I'll fix now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I said it should fail I was thinking that I had the right side of the comparison right [1, 2, 3]
@josevalim @ericmj Those two issues are addressed. I didn't simplify the test really, but just fixed it to use |
❤️ 💚 💙 💛 💜 |
@josevalim @zachdaniel (sorry for explicit mention but I don't know if you receive notification for closed PR) I really would like to have also
|
Implements
Access.find/1
andAccess.filter/1
. Examples and documentation provided in the code (aligned with examples and documentation forAccess.at/1
.