# Seeking an "isvector" function

**URL:** <https://discourse.julialang.org/t/seeking-an-isvector-function/80173>\
**Category:** New to Julia\
**Created:** [April 27, 2022, 10:40pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173 "2022-04-27T22:40:04Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![chadagreene](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chadagreene/32/34924_2.png) [@chadagreene](https://discourse.julialang.org/u/chadagreene)\
**Post date:** [April 27, 2022, 10:40pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/1 "2022-04-27T22:40:05Z")

</div>

I’m seeking a function similar to Matlab’s `isvector`, to determine whether a variable is one dimensional.

### These are the cases where `isvector(a)` should return `true`:

```julia
a = [1 2 3 4]
a = [1, 2, 3, 4] 
a = [1 2 3 4]'
a = [1, 2, 3, 4]'

```

### This case should return `false`:

```julia
a = [1 2; 3 4]

```

I’ve tried

```julia
sum(size(a).==1)

```

but that fails for `a = [1, 2, 3, 4]`.

This seems like a very simple problem. Is there a straightforward way to solve it?

---

<div class="post-metadata">

**Author:** ![fredrikekre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredrikekre/32/1688_2.png) [@fredrikekre](https://discourse.julialang.org/u/fredrikekre)\
**Post date:** [April 27, 2022, 10:54pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/2 "2022-04-27T22:54:22Z")

</div>

You can check `a isa AbstractVector`.

However, it feels a bit like an [XY problem](https://en.wikipedia.org/wiki/XY_problem). I have never (or very rarely) had to check for something like this – normally you would utilize multiple dispatch instead, and have one method for `::AbstractVector` and another one for `::AbstractMatrix`, for example. Since this is not really possible in matlab you have to use `isvector` etc, but in Julia there are better ways, usually.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 27, 2022, 10:54pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/3 "2022-04-27T22:54:29Z")

</div>

Agreed with @fredrikekre.

However, if forced to solve the problem as stated, I’d probably do

~~isvector(v) = prod(size(v)) == length(v)~~

~~This will only be true in the case where `size(v)` is only 1s except one entry which should be the length.~~

Nope, that’s not right, look at @stevengj’s solution

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 27, 2022, 10:57pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/4 "2022-04-27T22:57:26Z")

</div>

It sounds like you want

```julia
is1d(a) = count(>(1), size(a)) ≤ 1

```

but I agree with @fredrikekre that something seems odd here. Under what circumstances in Julia would you want code to accept `[1,2,3,4]` and `[1 2 3 4]` but not other arrays? Unlike Matlab, we have a “true” 1d array type in `AbstractVector` (as opposed to 1-column matrices).

---

<div class="post-metadata">

**Author:** ![chadagreene](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chadagreene/32/34924_2.png) [@chadagreene](https://discourse.julialang.org/u/chadagreene)\
**Post date:** [April 27, 2022, 11:14pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/5 "2022-04-27T23:14:54Z")

</div>

@stevengj Your `is1d` solution is exactly what I’m looking for. However, your skepticism about what I’m trying to do suggests there might be some fundamental concept I need to learn.

Here’s the case: I’m writing a function that will perform different calculations, based on whether the user enters:

1. a single point,
2. a set of points with explicitly defined `lat`, `lon` locations, or
3. a grid of points defined by a 1D `lon` row vector and a 1D `lat` column vector (sorry for the Matlab terminology).

Here’s what I have so far:

```julia
function myfunction(lat,lon)

    if (length(lat)==1) & (length(lon)==1) 
        # calculate for a single point

    elseif isequal(size(lat),size(lon))    
        # arbitrary array of points, or fully-defined grid of points 

    elseif length(lat)>1 & length(lon)>1 & is1d(lat) & is1d(lon)
        # two 1D vectors of different size define a grid

    else 
        error("Inputs do not make sense.")
    end
end

```

Is there a different way I should be going about this?

---

<div class="post-metadata">

**Author:** ![mbaz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbaz/32/17295_2.png) [@mbaz](https://discourse.julialang.org/u/mbaz)\
**Post date:** [April 27, 2022, 11:26pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/6 "2022-04-27T23:26:03Z")

</div>

What you’re doing works, but multiple dispatch offers a different way to do it. For example:

```julia
function myfunction(lat::T1, lon::T2) where {T1, T2 <: Real}
    # calculate for a single point
end

```

is called whenever `lat` and `lon` are real numbers. This is a “method” of the function `myfunction`. You would define other methods for the other two cases.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 28, 2022, 12:12am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/7 "2022-04-28T00:12:17Z")

</div>

> [@mbaz](#):
>
> `function myfunction(lat::T1, lon::T2) where {T1, T2 <: Real}`

Much simpler to write

```julia
function myfunction(lat::Real, lon::Real)

```

But in any case, the point remains that you should just write different methods to do different things for different argument types.

Note that if you just want to “vectorize” your function over a bunch of points, you can just use dot calls, e.g. `myfunction.(lat', lot)` to generate a matrix from a grid of points defined by a row vector `lat'` and a column vector `lot`.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [April 28, 2022, 12:34am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/8 "2022-04-28T00:34:49Z")

</div>

GMT.jl has one with that name.  
[https://github.com/GenericMappingTools/GMT.jl/blob/master/src/common\_options.jl#L3225](https://github.com/GenericMappingTools/GMT.jl/blob/master/src/common_options.jl#L3225)

I need it to allow users pass input options to GMT modules either as a [x1, x2, x3] or [x1 x2 x3]

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 28, 2022, 12:44am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/9 "2022-04-28T00:44:35Z")

</div>

> [@joa-quim](#):
>
> I need it to allow users pass input options to GMT modules either as a [x1, x2, x3] or [x1 x2 x3]

If you’re going to allow those two options, why not allow any iterable container?

---

<div class="post-metadata">

**Author:** ![liuyxpp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/liuyxpp/32/9870_2.png) [@liuyxpp](https://discourse.julialang.org/u/liuyxpp)\
**Post date:** [April 28, 2022, 2:14am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/10 "2022-04-28T02:14:36Z")

</div>

Maybe I miss something. But why not

```julia
is1d(a) = ndims(a) == 1 

```

Edit: I now understand that it is possible to have nx1 or 1xn matrix or other high dimensional array…  
But I also suggest to deal the simple vector if possible.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 28, 2022, 2:24am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/11 "2022-04-28T02:24:54Z")

</div>

Because row vectors are treated as 1-row matrices with `ndims == 2`, and @chadagreene wanted to distinguish them.

```julia
julia> ndims([1 2 3])
2

julia> ndims([1,2,3])
1

julia> ndims([1,2,3]')
2

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [April 28, 2022, 5:19am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/12 "2022-04-28T05:19:15Z")

</div>

> [@stevengj](#):
>
> you can just use dot calls, e.g. `myfunction.(lat', lot)` to generate a matrix from a grid of points

Agreed, this is most likely a job for broadcasting. But based on @chadagreene’s description it should be

```julia
myfunction.(lat, lot) 

```

In fact, even Matlab now has some limited broadcasting capabilities (implicit, though), so even there you can avoid the excruciating dimensions checks sometimes.

> [@joa-quim](#):
>
> I need it to allow users pass input options to GMT modules either as a [x1, x2, x3] or [x1 x2 x3]

That looks a bit like it could be redesigned. Can’t you just wrap the input in `vec`? In Matlab you would do `input = input(:)`.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [April 28, 2022, 6:34am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/13 "2022-04-28T06:34:42Z")

</div>

I should add that it is deeply ingrained in the Matlab psyche (I should know) to pass various arrays into a function, and then wrangle with their shapes, trying to figure out what to return. This is done in order to expoit ‘vectorization’ in the innermost, ‘built-in’, functions, and to get good performance.

You don’t need to do that in Julia, you can write your code for scalar inputs, and then leave all the trouble with matching of shapes and expansion of dimensions to the broadcasting machinery:

```julia
function myfunction(lat::Real,lon::Real)
    # calculations for a single point
end

```

And then call it with `myfunction.(latarray, lonarray)` (no need to explicitly use multiple dispatch and create multiple methods), which will handle all shapes and dimensions you want, as long as they ‘make sense’ and fit each other. This tends to make your code simpler, more general, and more robust.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [April 28, 2022, 6:09pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/14 "2022-04-28T18:09:21Z")

</div>

Maybe, but …

> <https://github.com/GenericMappingTools/GMT.jl/blob/master/src/common_options.jl#L140>

This function tests for the allowed types that can be provided to build GMT’s **-R** (BoundingBox) option. There I’m not even using `isvector` as I should but instead checking the `VMr` type (a `Union{Vector{<:Real}, Matrix{<:Real}}`) thus leaving open a path to an uncaught usage error. So yes, checking for _vector sensu lato_ seems useful to me.

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [April 29, 2022, 8:51am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/15 "2022-04-29T08:51:24Z")

</div>

But even in that example you can see that a Vector or Matrix with 1 dimension of size \>1 aren’t the only vector-like things. You have a check on the same line for a Tuple…

Probably, the best bet in this example, as with nearly every case involving type checking, is to use multiple dispatch. Anything which you (as the designer of this interface) deem “close enough to a vector” should behave like a vector, which usually means _converting it to a vector_ and passing it to another method to handle.

That could look something like

```julia
build_opt_R(val) = build_opt_R(x, Symbol())
build_opt_R(val, symb::Symbol) = "" # default case, anything unexpected goes here

function build_opt_R(val::Union{String, Symbol}, symb::Symbol)
    r = string(Val)
    if (r == "global") R = " -Rd"
    elseif (r == "global360") R = " -Rg"
    elseif (r == "same") R = " -R"
    else R = " -R" * r
    end
    return R
end

# helper function. Pretty sure the else case does the same as arg2str...
function _some_name(val, symb)
    R = symb ∈ (:region_llur, :limits_llur, :limits_diag, :region_diag) ?
        " -R" * @sprintf("%.15g/%.15g/%.15g/%.15g", val[1], val[3], val[2], val[4]) :
        " -R" * @sprintf("%.15g/%.15g/%.15g/%.15g", val[1], val[2], val[3], val[4])
    return R
end

# most restrictive/specific case. Only tuples of real numbers of length 4 or 6 are allowed
build_opt_R(val::Union{NTuple{4, Real}, NTuple{6, Real}}, symb::Symbol) = _some_name(val, symb)*"+r"
# for any array, try turning it into a tuple. If it's an acceptable kind, 
# it'll go through the method above. Otherwise it'll go to the default case.
# Worth noting this is a type unstable conversion, but this doesn't seem like it 
# needs to be performant. If it does, reverse the cases so that Vector
# is the base case, and Tuple and AbstractArray both forward to it with vec!
build_opt_R(val::AbstractArray, symb::Symbol) = build_opt_R(Tuple(val), symb) # can be widened pretty easily to include any iterable if you want. 
build_opt_R(val::GDtype, symb::Symbol) = _some_name(val[1].bbox, symb)
build_opt_R(val::GMTdataset, symb::Symbol) = _some_name(val.ds_bbox, symb)
build_opt_R(val::GItype, symb::Symbol) = _some_name(val.range[1:4], Symbol()) # I'm cheating here with symb to copy the behavior in the original

```

Pretty sure this does the same thing, but I haven’t run it through your test suite. Also, apologies for `_some_name`… it’s late 😴  
Of course, I don’t mean to suggest you should always allow all `AbstractArray`s of any dimension all over your code, and probably the above is way too permissive. Just an example.  
It’s just as common to just force the caller to explicitly convert from a matrix (or whatever) to a vector and not bother with any of this.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [April 29, 2022, 7:18pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/16 "2022-04-29T19:18:33Z")

</div>

Thanks for your reply and work to break the type testing function into multi-dispached ones. I do use it in several parts of the code but in examples like this why is the multi-dispatch better? At the end the binary code will have to keep references to 9 functions instead of just 1. Apply this multiplying factor to other similar cases. Why keeping some hash table to a lot more of function names and their entry points across to the entire package is better than be more savvy and use only one to do the same job?

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [May 1, 2022, 7:49pm UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/17 "2022-05-01T19:49:47Z")

</div>

> [@joa-quim](#):
>
> I do use it in several parts of the code but in examples like this why is the multi-dispatch better?

It’s not inherently better, but it does have some advantages. For example, it is more extensible. If you want to add behavior for a new type, it’s easy to add another method. As the author of the package, it could seem trivial to just add another if-else condition, but if I am a _user_, I can’t modify the source code without forking the repo. However, I can easily add another method to add the behavior I want. In general, that’s what makes julia packages much more easily composable than packages in other languages. Even as the author though, you may find it easier to add new behaviors (or adjust existing ones) with new methods rather than new branches. There is also the advantage that I only have one method above that does any real “work”. All of the other methods just forward to it. That is a common pattern, and usually makes it easier to debug.

> [@joa-quim](#):
>
> At the end the binary code will have to keep references to 9 functions instead of just 1. Apply this multiplying factor to other similar cases. Why keeping some hash table to a lot more of function names and their entry points across to the entire package is better than be more savvy and use only one to do the same job?

I never think about the number of function references or anything like that, and I believe that in practice that never ends up mattering, so feel free to take my opinion with a grain of salt. _However_, I think this idea of a multiplying factor may be based on a misunderstanding. Your original function isn’t just one function either. When it is called with a `Tuple`, a special case of it is compiled for that tuple type. When it is called with a string, a special case is compiled for a `String`, etc. Although there is only one method defined, there will be many `MethodInstances` that get compiled (unless you specify `@nospecialize` on the arguments I suppose). So while there is only one method in the if-else approach, the number of method-instances will be exactly the same, so I don’t think there are any practical savings anyway.

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [July 1, 2022, 8:42am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/18 "2022-07-01T08:42:18Z")

</div>

I would like to define a function with three methods.  
But I fail, how can I merge the three different functions,  
into one function with three methods?

```julia
# distinguish between vector and matrix
function MyLib_isvectorA(_val::Vector{<:Number})
    return count(>(1), size(_val)) ≤ 1
end  
  
function MyLib_isvectorB(_val::Array{<:Number})
    return count(>(1), size(_val)) ≤ 1
end  

function MyLib_isvectorC(_val::Number)
    return count(>(1), size(_val)) ≤ 1
end  

```

---

<div class="post-metadata">

**Author:** ![albheim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albheim/32/34660_2.png) [@albheim](https://discourse.julialang.org/u/albheim)\
**Post date:** [July 1, 2022, 9:00am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/19 "2022-07-01T09:00:36Z")

</div>

This works for me, but I might be missing what you are actually asking for here?

```julia-repl
julia> a(x::Vector{<:Number}) = 1
a (generic function with 1 method)

julia> a(x::Array{<:Number}) = 2
a (generic function with 2 methods)

julia> a(x::Number) = 3
a (generic function with 3 methods)

julia> a([3;])
1

julia> a([3;;])
2

julia> a(3)
3

```

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [July 1, 2022, 9:30am UTC](https://discourse.julialang.org/t/seeking-an-isvector-function/80173/20 "2022-07-01T09:30:37Z")

</div>

Thanks for the fast response!  
How can I define `MyLib_isvector()` in a way that it except three input types:  
a) `Vector{<:Number}`  
b) `Matrix{<:Number}`  
c) `::{<:Number}`

[Next page](https://discourse.julialang.org/t/seeking-an-isvector-function/80173.md?page=2)
