Manual version
With this option, you manually add a row for each attribute, reusing the same expression. See below how to automatize this for all fieldnames at once.
Create a condition for each attribute manually
You should concatenate a row for each attribute, connecting them using pipes: ||
. Better use an if-condition: if >0
, then show the value, otherwise simply use an empty sting ''
. The expression looks like this:
if ("grassland" > 0, 'grassland: '||"grassland"||'%' || '\n' , '' ) ||
if ("forest" > 0, 'forest: '||"forest"||'%' || '\n' , '' ) ||
if ("steppe" > 0, 'steppe: '||"steppe"||'%' || '\n' , '' ) ||
if ("other" > 0, 'ohter: '||"other"||'%' || '\n' , '' )
I added the text field you asked for above using this exptession and the attribute table below so you can compare the values. The first attribute (grassland
) has a value of 0
and is thus not added to the text field:
Automatize the expression
If you have dozens of attributes, it can get annoying to copy the expression and each time replace the name of the attribute three times per line. You can automatize this using this expression (to define the order in which the attributes appear, see below):
replace (
array_to_string (
array_foreach (
array_filter(
map_akeys( attributes( ) ),
@element not in ('id', 'id2')
),
eval (
replace (
'if ("an" > 0, ''an: ''||"an"||''%'' || ''\n'' , '''' ) ',
'an',
@element
)
)
)
),
',',
''
)
Explanation
With map_akeys( attributes( ) )
(line 5) you get an array with all attribute-names. You can filter out some names with array_filter
: in line 6, define which attribute names to skip (here: 'id'
and 'id2'
).
Then use array_foreach
to iterate: for each of the remaining attribute name, perform the following function.
Define the pattern you used in the version above to create your output as a string (line 10). Instead of the attribute name, insert a generic name (here: an
for attribute name), then replace that with the current element (each of the attribute names). Up to now, you have created a string that returns for each element of the array what you created before manually. Use eval
to evaluate the string - the string should be converted to a working function. Output is an array of strings. Convert the array to sting (line 2) and delete all the commas (line 1 and 17/18 respectively):
Output with the automatized expression: as you see, all 0-values are skipped, as well as the 2 attributes we defined to skip (id
and id2
). Only issue is that attributes are sorted in alphbetic order - for other sorting options, see in what follows:
Change sort order
Ordering in the same sequence as fields appear in attribute table
See this answer to order the attributes not in alphabetic order as is the default using the function attributes( )
, but in the same order as they appear in the attribute table
Custom defined ordering
To sort the entries in any arbitray order, you can use array_prioritize( )
(available since QGIS 3.20) to define manually a custom order.
Ordering depending on the attribute values
To order the results depending on their values (from highest to lowest value), modify the expression this way:
regexp_replace(
array_to_string (
with_variable (
'output',
array_foreach (
array_filter(
map_akeys( attributes( ) ),
@element not in ('fid', 'id')
),
'<sort>' || right (left ('00' || format_number (attribute (@element),1),7),5) || '</sort>' ||
eval (
replace (
'if ("el" > 0, ''el: ''||"el"||''%'' || ''\n'' , '''' ) ',
'el',
@element
)
)
),
array_sort (@output,0)
)
),
'(,)|(<sort>.*</sort>)',
''
)
The result, showin the attribute table (bottom) and a text field with only those attribute values >0
from the current atlas feature, ordered from highest to smallest attribute value: