Traversal

This code demonstrates how to use and extend advanced optics. The examples are taken from the README.md of the specter clojure library. Many of the features here are experimental, please consult the docstrings of the involved optics.

using Test
using Accessors

import Accessors: modify, OpticStyle
using Accessors: ModifyBased, SetBased, setindex

Increment all even numbers

We have the following data and the goal is to increment all nested even numbers.

data = (a = [(aa=1, bb=2), (cc=3,)], b = [(dd=4,)])
(a = NamedTuple[(aa = 1, bb = 2), (cc = 3,)], b = NamedTuple{(:dd,), Tuple{Int64}}[(dd = 4,)])

To acomplish this, we define a new optic Vals.

function mapvals(f, d)
    Dict(k => f(v) for (k,v) in pairs(d))
end

mapvals(f, nt::NamedTuple) = map(f, nt)

struct Vals end
OpticStyle(::Type{Vals}) = ModifyBased()
modify(f, obj, ::Vals) = mapvals(f, obj)
modify (generic function with 11 methods)

Now we can increment as follows:

out = @set data |> Vals() |> Elements() |> Vals() |> If(iseven) += 1

@test out == (a = [(aa = 1, bb = 3), (cc = 3,)], b = [(dd = 5,)])
Test Passed
  Expression: out == (a = [(aa = 1, bb = 3), (cc = 3,)], b = [(dd = 5,)])
   Evaluated: (a = NamedTuple[(aa = 1, bb = 3), (cc = 3,)], b = NamedTuple{(:dd,), Tuple{Int64}}[(dd = 5,)]) == (a = NamedTuple[(aa = 1, bb = 3), (cc = 3,)], b = NamedTuple{(:dd,), Tuple{Int64}}[(dd = 5,)])

Append to nested vector

data = (a = 1:3,)
out = @modify(v -> vcat(v, [4,5]), data.a)

@test out == (a = [1,2,3,4,5],)
Test Passed
  Expression: out == (a = [1, 2, 3, 4, 5],)
   Evaluated: (a = [1, 2, 3, 4, 5],) == (a = [1, 2, 3, 4, 5],)

Increment last odd number in a sequence

data = 1:4
out = @set data |> filter(isodd, _) |> last += 1
@test out == [1,2,4,4]

### Map over a sequence

data = 1:3
out = @set data |> Elements() += 1
@test out == [2,3,4]
Test Passed
  Expression: out == [2, 3, 4]
   Evaluated: [2, 3, 4] == [2, 3, 4]

Increment all values in a nested Dict

data = Dict(:a => Dict(:aa =>1), :b => Dict(:ba => -1, :bb => 2))
out = @set data |> Vals() |> Vals() += 1
@test out == Dict(:a => Dict(:aa => 2),:b => Dict(:bb => 3,:ba => 0))
Test Passed
  Expression: out == Dict(:a => Dict(:aa => 2), :b => Dict(:bb => 3, :ba => 0))
   Evaluated: Dict(:a => Dict(:aa => 2), :b => Dict(:bb => 3, :ba => 0)) == Dict(:a => Dict(:aa => 2), :b => Dict(:bb => 3, :ba => 0))

Increment all the even values for :a keys in a sequence of maps

data = [Dict(:a => 1), Dict(:a => 2), Dict(:a => 4), Dict(:a => 3)]
out = @set data |> Elements() |> _[:a] += 1
@test out == [Dict(:a => 2), Dict(:a => 3), Dict(:a => 5), Dict(:a => 4)]
Test Passed
  Expression: out == [Dict(:a => 2), Dict(:a => 3), Dict(:a => 5), Dict(:a => 4)]
   Evaluated: [Dict(:a => 2), Dict(:a => 3), Dict(:a => 5), Dict(:a => 4)] == [Dict(:a => 2), Dict(:a => 3), Dict(:a => 5), Dict(:a => 4)]

Retrieve every number divisible by 3 out of a sequence of sequences

data = [[1,2,3,4], Int[], [5,3,2,18], [2,4,6], [12]]
optic = @optic _ |> Elements() |> Elements() |> If(x -> mod(x, 3) == 0)
out = getall(data, optic)
@test out == [3, 3, 18, 6, 12]
@test eltype(out) == Int
Test Passed
  Expression: eltype(out) == Int
   Evaluated: Int64 == Int64

Increment the last odd number in a sequence

data = [2, 1, 3, 6, 9, 4, 8]
out = @set data |> filter(isodd, _) |> _[end] += 1
@test out == [2, 1, 3, 6, 10, 4, 8]
@test eltype(out) == Int
Test Passed
  Expression: eltype(out) == Int
   Evaluated: Int64 == Int64

Remove nils from a nested sequence

data = (a = [1,2,missing, 3, missing],)
optic = @optic _.a |> filter(!ismissing, _)
out = optic(data)
@test out == [1,2,3]
Test Passed
  Expression: out == [1, 2, 3]
   Evaluated: Union{Missing, Int64}[1, 2, 3] == [1, 2, 3]

This page was generated using Literate.jl.