M.E.
M.E.

Reputation: 5495

How to get the maximum value of the item of a struct when you have an array?

I have the following structure in Julia and I create an array with it.

julia>     struct myStruct
              a::Int
              b::Int
              c::String
           end

julia>     myArray = myStruct.(1:10,11:20,"ABC")
10-element Array{myStruct,1}:
 myStruct(1, 11, "ABC") 
 myStruct(2, 12, "ABC") 
 myStruct(3, 13, "ABC") 
 myStruct(4, 14, "ABC") 
 myStruct(5, 15, "ABC") 
 myStruct(6, 16, "ABC") 
 myStruct(7, 17, "ABC") 
 myStruct(8, 18, "ABC") 
 myStruct(9, 19, "ABC") 
 myStruct(10, 20, "ABC")

What shall I do in Julia to get the maximum value of a?

Is it recommended to first getting a 2 column array with the first two values of the struct and then use findmax(my2colArray[:,1]) to find the maximum value?

I have three questions to understand how shall I do this:

  1. If getting the array first is needed, how do I get efficiently that 2 column array?
  2. If it is not needed, how would I get the maximum value of a directly from the array of structs?
  3. The string will contain a maximum of 50 characters, and they will be ASCII (no UTF-8). Shall I fix the length of the string somehow to improve performance?

Upvotes: 1

Views: 922

Answers (2)

DNF
DNF

Reputation: 12654

The easiest way to get the max value of a is, as @fredrikekre writes:

maxval = maximum(x->x.a, arr)

Unfortunately, this does not give you the index of that value, which you also asked for in a comment.

Ideally, you could use argmax or findmax instead:

(maxval, maxind) = findmax(x->x.a, arr)  # <= This does not work!!

Currently, at version 1.2 of Julia, this does not work.

There may be some other clever solution, but my advice is to just write a loop yourself, it's easy and educational!

To address your questions:

0: (This was not a question) Remember to always name your types with UpperCamelCase: so MyStruct, not myStruct.

  1. No, you don't need this, and it's not a good solution. (Also I don't know why you want a 2-column vector, when you only are looking for the max of a). But if you really want it anyway:

    v = getproperty.(x, [:a :b])

  2. For max value, see the answer by @fredrikekre, for max index see below.

  3. No, I don't think so.

Write your own loop to get the max index and value. It's easy and fun, and you learn to write your own fast Julia code:

function find_amax(arr::AbstractArray{MyStruct})
    isempty(arr) && ArgumentError("reducing over an empty collection is not allowed")
    maxind, maxval = firstindex(arr), first(arr).a
    for (i, x) in enumerate(arr)
        if x.a > maxval
            maxind, maxval = i, x.a
        end
    end
    return maxval, maxind
end

There is a small inefficiency in the code above, the first value and index of x is read twice. If you want even faster performance, you can figure out a way to avoid that.

As for performance, this loop is about as fast as maximum(x->x.a, arr), and more than 60x as fast as building the 2-column matrix you asked for in question 1.

The main lesson is: You don't need to look for some clever "built-in" solution that you can plug your problem into. If you cannot quickly find one, just make your own, it will most likely be faster.

Upvotes: 2

fredrikekre
fredrikekre

Reputation: 10984

You can use the maximum function. maximum also takes a function, which you, in this case, can use to sort by the a field:

julia> struct myStruct
           a::Int
           b::Int
           c::String
       end

julia> myArray = myStruct.(21:30,11:20,"ABC");

julia> val = maximum(x -> x.a, myArray)
30

(Slightly modified your example to make the maximum value and the index different).

Upvotes: 4

Related Questions