Exploring The QGIS Expression Engine, Part 9: Use All The Fields

 

A friend asked me a few days ago how can he get the sum of all fields in a layer. He had a layer where all the fields were numeric and he wanted to create a sum like R's dplyr::across allows. Now that's an interesting question, not because it's a common problem, but because it got me thinking about automatically accessing fields in a QGIS layer, i.e. without knowing field names.


 

We can get the values of all of our fields with a relatively simple expression:

    map_avals( attributes( ))

 

 

What it does, is return an array of all the values. You can check how the fields are ordered and match a value to a key with a similar expression:

    map_akeys( attributes( )) 
 

 
 
Now I'm going to make some assumptions, mainly that you want to perform a calculation on all of you numeric fields, You can notice that the values in the first array are a mix of strings and numbers, so it's a mixed array.  if we want to use just the numeric fields we can't simply use array_sum, since we can't sum strings.
 
This is where we complicate things, the QGIS expression engine doesn't have a is_numeric\is_string\type\class function, so we can't really check if our values are numeric... or can we?
We are going to combine a few functions to get our list of numeric values:
 
    1. array_filter - which is going to filter only relevant values from our array
    2. to_real - which will try to convert all of our fields to a number
    3. try - which is going try, and tell us when the conversion fails.
 
Wrap our original expression in these, and we can get an array of only numerical values
 
    array_filter(
        map_avals( attributes()),
        try( to_real( @element),false)
    )
 
 


 
 We can wrap this expression with array_sum and get the sum of all our numerical values. the downside of not being able to check the type of each element is that the filtered array won't contain 0's, since the expression engine evaluates false as 0 and vice versa, so filter functions expect either true\false or 1\0.
Assuming you may want to use the arrays we create here for calculations that may need to include 0's as well, we are going to need a workaround for that, A really simple workaround at that:

array_filter(
    map_avals( attributes()),
    @element = 0 or try( to_real( @element),false)
)


And finally we can the sum of all the numeric values like this:
 
    array_sum(
        array_filter(
            map_avals( attributes()),
            @element = 0 or try( to_real( @element),false)
        )
    )


If you have calculations or actions you may want to do across all of your fields this seems to be the easiest method to get a single value without specifying or even knowing all field names.
You can also use map_akeys(attributes()) and wrap that in a for loop since attributes is a dictionary\JSON of the current feature, and that might be a subject for another post, but not today.

You can check out the other posts in this series and learn more about the QGIS expression engine, and if you have your own cool uses for expression I'd love to hear about them in the comments.

Exploring The QGIS Expression Engine, Part 1: Getting Values From JSON & HSTORE

Exploring The QGIS Expression Engine, Part 2: What's Missing From Select By Location 

Exploring The QGIS Expression Engine, Part 3: Writing Custom Expression Functions

Exploring The QGIS Expression Engine, Part 4: Selecting By Attributes And Location With One Expression

Exploring The QGIS Expression Engine, Part 5: Fun With Arrays

Exploring The QGIS Expression Engine, Part 6: Creating And Using Variables

Exploring The QGIS Expression Engine, Part 7: Cross-Layer Relationships 

Exploring The QGIS Expression Engine, Part 8: Expression Controlled Styles

Comments