Skip to content
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

Rework the getbyattrib function to select more elements. Make filter and findall aliases of it. #1239

Merged
merged 4 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/show_pretty_datasets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ function batch_compacttype(types::Vector{Any}, maxwidths::Vector{Int})
end
end

#=
function batch_compacttype(types::Vector{Any}, maxwidth::Int)
cache = Dict{Type, String}()
return map(types) do T
Expand All @@ -216,6 +217,7 @@ function batch_compacttype(types::Vector{Any}, maxwidth::Int)
end
end
end
=#

"""
compacttype(T::Type, maxwidth::Int=8, initial::Bool=true)
Expand Down
72 changes: 54 additions & 18 deletions src/utils_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1892,17 +1892,28 @@ end

# ---------------------------------------------------------------------------------------------------
"""
getbyattrib(D::Vector{<:GMTdataset}[, index::Bool]; kw...)
getbyattrib(D::Vector{<:GMTdataset}[, index::Bool=false]; kw...)

Take a GMTdataset vector and return only its elememts that match the condition(s) set by the `attrib` keywords.
or

filter(D::Vector{<:GMTdataset}; kw...)
or

findall(D::Vector{<:GMTdataset}; kw...)

Take a GMTdataset vector and return only its elements that match the condition(s) set by the `kw` keywords.
Note, this assumes that `D` has its `attrib` fields set with usable information.

NOTE: Instead of ``getbyattrib`` one case use instead ``filter`` (==> `index=false`) or ``findall`` (==> `index=true`)

### Parameters
- `attrib` or `att`: keyword with the attribute `name` used in selection. It can be a single name as in `att="NAME_2"`
or a NamedTuple with the attribname, attribvalue as in `att=(NAME_2="value")`. Use more elements if
wishing to do a composite match. E.g. `att=(NAME_1="val1", NAME_2="val2")` in which case oly segments
matching the two conditions are returned.
- `val` or `value`: keyword with the attribute ``value`` used in selection. Use this only when `att` is not a NamedTuple.
- `attrib name(s)=value(s)`: Easier to explain by examples: `NAME="Antioquia"`, select all elements that have
that attribute/value combination. `NAME=("Antioquia", "Caldas"), pick elements that have those `NAME` attributes.
Add as many as wished. If using two `kwargs` the second works as a condition. ``(..., NAME=("Antioquia", "Caldas"), feature_id=0)``
means select all elements from ``Antioquia`` and ``Caldas`` that have the attribute `feature_id` = 0.
- `attrib` or `att`: (OLD SYNTAX) A NamedTuple with the attribname, attribvalue as in `att=(NAME_2="value",)`.
Use more elements if wishing to do a composite match. E.g. `att=(NAME_1="val1", NAME_2="val2")` in which
case only segments matching the two conditions are returned.
- `index`: Use this `positional` argument = `true` to return only the segment indices that match the `att` condition(s).

### Returns
Expand All @@ -1911,31 +1922,52 @@ Or `nothing` if the query results in an empty GMTdataset

## Example:

D = getbyattrib(D, attrib="NAME_2", val="Porto");
D = filter(D, NAME_2="Porto");
"""
function getbyattrib(D::Vector{<:GMTdataset}, ind_::Bool; kw...)::Vector{Int}
# This method does the work but it's not the one normally used by the public.
# It returns the indices of the selected segments.
(isempty(D[1].attrib)) && (@warn("This datset does not have an `attrib` field and is hence unusable here."); return Int[])
((_att = find_in_kwargs(kw, [:att :attrib])[1]) === nothing) && error("Must provide the `attribute` NAME.")
if isa(_att, NamedTuple)
atts, vals = string.(keys(_att)), string.(values(_att))
dounion = Int(1e9) # Just a big number
if ((_att = find_in_kwargs(kw, [:att :attrib])[1]) !== nothing) # For backward compat.
if !isa(_att, NamedTuple)
((val = find_in_kwargs(kw, [:val :value])[1]) === nothing) && error("Must provide the `attribute` VALUE.")
end
atts, vals = isa(_att, NamedTuple) ? (string.(keys(_att)), string.(values(_att))) : ((string(_att),), (string(val),))
else
atts = (string(_att),)
((val = find_in_kwargs(kw, [:val :value])[1]) === nothing) && error("Must provide the `attribute` VALUE.")
vals = (string(val),)
# FCK unbelievable case. Julia can be f. desparing. It took me an whole afternoon to make this work.
#d = KW(kw); Keys = keys(d);
#Keys[1] => Internal Error: MethodError: no method matching getindex(::Base.KeySet{Symbol, Dict{Symbol, Any}}, ::Int64)
count, kk = 0, 1
v = values(kw)
for k = 1:numel(kw) count += (isa(values(v[k]), Tuple)) ? length(values(v[k])) : 1 end
atts, vals = Vector{String}(undef, count), Vector{String}(undef, count)

_keys = string.(keys(kw))
for k = 1:numel(kw)
vv = values(v[k])
if (isa(vv, Tuple))
atts[kk:kk+length(vv)-1] .= _keys[k]
vals[kk:kk+length(vv)-1] .= string.(vv)
kk += length(vv)
else
atts[kk], vals[kk] = _keys[k], string(vv)
kk += 1
end
(k == 1) && (dounion = kk) # Index that separates the unions from the interceptions
end
end

indices::Vector{Int} = Int[]
ky = keys(D[1].attrib)
for n = 1:numel(atts)
ky = keys(D[1].attrib)
((ind = findfirst(ky .== atts[n])) === nothing) && return Int[]
((ind = findfirst(ky .== atts[n])) === nothing) && continue#return Int[]
tf = fill(false, length(D))
for k = 1:length(D)
(!isempty(D[k].attrib) && (D[k].attrib[atts[n]] == vals[n])) && (tf[k] = true)
end
if (n == 1) indices = findall(tf)
else indices = intersect(indices, findall(tf))
else indices = (dounion > n) ? union(indices, findall(tf)) : intersect(indices, findall(tf))
end
end
return indices
Expand All @@ -1945,4 +1977,8 @@ function getbyattrib(D::Vector{<:GMTdataset}; kw...)::Union{Nothing, Vector{GMTd
# This is the intended public method. It returns a subset of the selected segments
ind = getbyattrib(D, true; kw...)
return isempty(ind) ? nothing : D[ind]
end
end

# ---------------------------------------------------------------------------------------------------
Base.:filter(D::Vector{<:GMTdataset}; kw...) = getbyattrib(D; kw...)
Base.:findall(D::Vector{<:GMTdataset}; kw...) = getbyattrib(D, true; kw...)
4 changes: 3 additions & 1 deletion test/test_common_opts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@
GMT.polygonlevels(D, ["a", "b", "c"], [1,2,missing], att="nome");
GMT.edit_segment_headers!(D, [1], "0");
GMT.getbyattrib(D, att="nome", val="a");
GMT.getbyattrib(D, att=(nome="a", nome2="b"));
filter(D, nome="a", nome2="b");
filter(D, nome=("a","b"));
findall(D, nome="a");
D = mat2ds([0 0; 1 1],["a", "b"]); D.header = "-Wred";
@test GMT.edit_segment_headers!(D, 'W', :get) == "red"
@test GMT.edit_segment_headers!(D, 'W', :set, "blue") == "-Wblue"
Expand Down
2 changes: 1 addition & 1 deletion test/test_misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
grdcontour(G, axis="a", color=cpt, pen="+c", fmt=:png, savefig="lixo")
D = grdcontour(G, cont=[-2,0,5], dump=true);

show(makecpt(C=:rainbow))
info(makecpt(C=:rainbow))

add2PSfile("Bla")
add2PSfile(["Bla", "Bla"])
Expand Down
Loading