Array of derived type: select entry


Currently in my code I have a 2D array

integer, allocatable :: elements(:,:)

and define some constants

integer, parameter :: TYP = 1
integer, parameter :: WIDTH = 2
integer, parameter :: HEIGHT = 3
! ...
integer, parameter :: NUM_ENTRIES = 10

and allocate something like


so I can access elements like

write(*,*) elements(WIDTH,100) ! gives the width of the 100th element

Now I would like to have not only integer but a mixture of types for every element.
So I define a derived type

type Element
    logical active
    integer type
    real width
    ! etc
end type

and use an array of Elements

type(Element), allocatable :: elements(:)

With the 2d array version I could call a subroutine telling it which entry to use.

subroutine find_average(entry, avg)
    integer, intent(in) :: entry   
    real, intent(out) :: avg
    integer i, 
    real s

    s = 0
    do i = lbound(elements,1), ubound(elements,1)
        if (elements(TYP,i) .gt. 0) s = s + elements(entry,i)
    end do
    avg = s/(ubound(elements,1)-lbound(elements,1))
end subroutine       

So I could call find_average(HEIGHT) to find the average height or pass WIDTH to get the average width.
(And my subroutines do more advanced things than finding the average height or width, this is just an example.)

Question: How can I use different types (as with the derived type) but also reuse my functions to work with different entries (as in the example subroutine)?

Best Solution

For the array case, instead of passing in arguments array and index i, you could pass in the single argument array (i). When you switch to having the derived type, similarly you could pass in variable_of_type % element rather than passing in the entire variable_of_type and somehow instructing the procedure which subelement it is supposed to work on. If the code needs to be different for the different types of elements (e.g., logical, integer, real), then you could write specific procedures for each, but call then with a common name via a generic interface block. The compiler has to be able to distinguish the procedures of the generic interface block by some characteristic of the arguments, here their type. For a code example in which the distinguishing characteristic is array rank see how to write wrapper for 'allocate'

EDIT: example code. does this do what you want?

module my_subs

   implicit none

   interface my_sum
      module procedure sum_real, sum_int
   end interface my_sum


subroutine sum_real (array, tot)
   real, dimension(:), intent (in) :: array
   real, intent (out) :: tot
   integer :: i

   tot = 1.0
   do i=1, size (array)
      tot = tot * array (i)
   end do
end subroutine sum_real

subroutine sum_int (array, tot)
   integer, dimension(:), intent (in) :: array
   integer, intent (out) :: tot
   integer :: i

   tot = 0
   do i=1, size (array)
      tot = tot + array (i)
   end do
end subroutine sum_int

end module my_subs

program test_dt

use my_subs

implicit none

type my_type
   integer weight
   real length
end type my_type

type (my_type), dimension (:), allocatable :: people
type (my_type) :: answer

allocate (people (2))

people (1) % weight = 1
people (1) % length = 1.0
people (2) % weight = 2
people (2) % length = 2.0

call my_sum ( people (:) % weight, answer % weight )
write (*, *)  answer % weight

call my_sum ( people (:) % length, answer % length )
write (*, *)  answer % length

end program test_dt