Le blog francophone consacré
aux technologies Esri

Optimiser ses expressions Arcade avec 5 fonctions souvent sous-utilisées

Arcade est un langage d'expression qui vous permet de créer du contenu personnalisé dans des fenêtres contextuelles, de formater des étiquettes, de créer des comportements dans les formulaires intelligents, de créer de nouvelles valeurs de données pour les cartes web et les tableaux de bord ou encore d'appliquer des règles dans divers flux de travail. J'ai déjà évoqué sur arcOrama les fonctions de base d'Arcade à partir d'exemples concrets ainsi que des cas d'usages plus avancés. Beaucoup d'entre vous utilise donc Arcade au quotidien car c'est simple et puissant.


Je vois beaucoup d'expressions Arcade réalisées par des utilisateurs ArcGIS et je les aide parfois à les améliorer. Au fil du temps, j'ai remarqué que certaines peuvent être simplifiés ou améliorées en utilisant des fonctions Arcade prêtes à l'emploi. La plupart du temps, les auteurs d'expressions ne connaissent pas certaines fonctionnalités d'Arcade qui leur manquent. Dans cet article, je vais évoquer 5 fonctions Arcade méconnues et donc sous-utilisées et j'essayerai de vous montrer comment elles peuvent améliorer vos expressions.

1. La fonction "IIf"

La fonction IIf renvoie une valeur si une expression conditionnelle prend la valeur true, et renvoie une autre valeur si la condition prend la valeur false. Ceci est particulièrement utile dans des scénarios simples tels que l'écriture d'une expression pour indiquer quand une valeur a atteint un seuil.

Généralement, dans vos expressions, vous utilisez des blocs if-else pour renvoyer des valeurs de manière conditionnelle. Par exemple, comme ceci :
// Si la valeur est supérieure à 100 retourner "fort"
// Sinon, retourner "faible"

var categorie = "";

if($feature.value > 100){
    categorie = "fort";
} else {
    categorie = "faible";
}
return categorie;

Cependant, la fonction IIf simplifie cela en deux lignes sans avoir besoin de plusieurs retours ou affectations de variables…
// Si la valeur est supérieure à 100 retourner "fort"
// Sinon, retourner "faible"

var categorie = IIF($feature.value > 100, "fort", "faible");
return categorie;
…ou à une seule ligne (grâce aux retours implicites).
IIF($feature.value > 100, "fort", "faible");
Bien entendu, il n'y a pas d'erreur à utiliser un bloc if-else pour renvoyer un contenu conditionnel. En fait, vous devez utiliser des instructions if-else si vous devez évaluer d'autres instructions qui suivent les mêmes conditions, y compris l'affectation de valeurs à des variables. Dans l'exemple suivant, la fonction IIf n'est pas appropriée car plusieurs affectations de variables ont lieu si la condition est vraie.
if($feature.value > 100){
  pourcentAuDessus = $feature.value - 100;
  categorie = "fort";
} else {
  category = "faible";
}
return `${categorie} (${pourcentAuDessus}%)`;
Retenez donc que lorsque vous avez à renvoyer une valeur basée sur une condition simple, IIf condensera et simplifiera votre expression.


2. La fonction "Text"

La fonction Text convertit une valeur de n'importe quel type de données Arcade en une valeur de type texte équivalente. Vous trouverez cette fonction particulièrement utile lors du formatage des dates, mais j'ai vu beaucoup d'expressions qui implémentent des logiques supplémentaires pour le formatage des nombres alors qu'elles pourraient être évitées simplement en tirant parti de Text.

Cela est par exemple vrai lorsqu'il s'agit d'arrondir des nombres. Vous vous demandez peut-être : pourquoi devrais-je utiliser Text pour arrondir les nombres alors qu'Arcade a déjà une fonction d'arrondi ? Par exemple, ce modèle est très courant :
Round(($feature.diplomes/$feature.PopulationAdulte) * 100, 1) + “%”;
// retourne "37.8%";
Cependant, Text vous offre une option % afin que vous n'ayez pas à faire le calcul de conversion d'un nombre décimal (0-1) en pourcentage.
Text($feature.diplomes/$feature.PopulationAdulte, "#.#%");
// retourne "37.8%";
Mais est-ce que votre expression Text est vraiment meilleure dans ce cas ? Oui car les deux approches sont différentes car Round renvoie un nombre alors Text renvoie une valeur de type texte. Voici pourquoi cette distinction est importante :

Round modifie la précision des nombres que vous utilisez pour les calculs dans une expression sans réellement formater la valeur. Text vous permet de formater un nombre en fonction d'un modèle de formatage. Bien que Text permette d'arrondir les nombres, il vous permet également de spécifier des séparateurs pour les grands nombres. En bref, Round peut arrondir vos nombres décimaux, mais la fonction Text arrondira et ajoutera des séparateurs de chiffres aux grands nombres.

Cependant, le plus grand avantage de l'utilisation Text est que la mise en forme définie dans la fonction sera conforme aux paramètres régionaux de l'application dans laquelle l'expression s'exécute. Round ne vous donne pas cet avantage.

Consultez les exemples suivants et notez les différences. Le texte renvoie toujours un résultat plus propre et plus correct.

var v = 2891.378268263982736;
Round(v, 2);
// retourne 2891.38 en anglais, sans séparateur de milliers
// retourne 2891.38 en français (mauvais séparateur de décimales), sans séparateur de milliers 

Text(v, "#,###.##");
// retourne "2,891.38" en anglais, avec séparateurs de milliers
// retourne "2 891,38" en français (bon séparateur de décimales et séparateurs de milliers
Si Text est préférable pour le formatage des nombres, quand dois-je utiliser Round ?

Dans de nombreux cas, la division de deux nombres donne un nombre avec de nombreuses décimales. Le niveau de précision qui en résulte est souvent trop élevé ou non requis par l'utilisateur final. Round vous aide à contrôler la précision de votre formule. Vous devez également l'utiliser Round lorsque vous devez renvoyer le résultat sous forme de nombre (comme dans un calcul de champ ou une valeur de rendu) ou continuer à l'utiliser comme entrée pour une autre fonction Arcade lorsque moins de précision est requise.

Je vois également parfois des utilisateurs se servir de IIf pour formater des valeurs au-dessus et en dessous d'une valeur seuil, comme ceci :
var precedente = $feature.pop2020;  // 3020
var actuelle = $feature.pop2023;   // 3333
var evolution = (actuelle - precedente) / precedente;

IIF(evolution > 0, "⬆", "⬇") + Round(evolution*100, 2) + "%";
// retourne "⬆10.4%" en anglais
// retourne "⬆10.4%" en français (séparateur de décimales incorrect)
Comme indiqué dans les résultats potentiels, cela entraînera un mauvais formatage dans de nombreux paramètres régionaux non anglais. Le paramètre de modèle de format de Text fournit en fait une option qui vous permet de spécifier un format différent pour les valeurs positives ou négatives. Séparez simplement les deux motifs par un point-virgule et Arcade fera le travail pour vous. Il n'y a pas besoin d'utiliser IIf du tout.
var precedente = $feature.pop2020;  // 3020
var actuelle = $feature.pop2023;   // 3333
var evolution = (actuelle - precedente) / precedente;

Text(evolution, "⬆#.#%;⬇#.#%");
// retourne "⬆10.4%" en anglais
// retourne "⬆10,4%" en français (séparateur de décimales correct)
Les images suivantes montrent comment l'utilisation Round entraînera des valeurs contextuelles incorrectes en fonction des paramètres régionaux.


3. La fonction "Decode"

Decode permet de trouver une correspondance dans un ensemble de codes et de renvoyer une description correspondant à ce code. Cette fonction peut être utilisée pour évaluer n'importe quelle expression à une valeur et comparer le résultat à une liste de valeurs attendues et renvoyer une description, une catégorie ou une autre valeur correspondante.

Par exemple, l'attribut CodeTech dans l'exemple suivant est une valeur codée. Je vois souvent des utilisateurs renvoyer la description du code en utilisant une séquence d'instructions if-else, comme ceci :
if($feature.CodeTech == 30){
  return "Réseau cuivre";
}
if($feature.CodeTech == 40){
  return "Réseau câble ";
}
if($feature.CodeTech == 50){
  return "Fibre";
}
if($feature.CodeTech == 60){
  return "Satellite";
}
if($feature.CodeTech == 70){
  return "Sans-fil terrestre";
}
if($feature.CodeTech == 90){
  return "Ligne électrique";
}
Bien entendu, cette syntaxe est valide et renvoie un résultat correct. Vous pouvez également condenser cette expression à l'aide de la fonction When :
When(
  $feature.CodeTech == 30, "Réseau cuivre",
  $feature.CodeTech == 40, "Réseau câble",
  $feature.CodeTech == 50, "Fibre",
  $feature.CodeTech == 60, "Satellite",
  $feature.CodeTech == 70, "Sans-fil terrestre",
  $feature.CodeTech == 90, "Ligne électrique",
  "Autre"
);
Ceci est également valide et renvoie un résultat correct. Cependant, comme le code provient d'un seul champ, vous pouvez simplement utiliser Decode pour renvoyer plus directement la description :
Decode($feature.CodeTech,
  30, "Réseau cuivre",
  40, "Réseau câble",
  50, "Fibre",
  60, "Satellite",
  70, "Sans-fil terrestre",
  90, "Ligne électrique",
  "Autre"
);
Votre expression est ainsi plus compacte et un peu plus facile à lire. Decode est préférable pour évaluer une seule expression et comparer le résultat à une liste de valeurs connues. When est préférable pour évaluer plusieurs expressions dans une séquence hiérarchisée et renvoyer une valeur une fois que l'une des déclarations est vraie.

Decode peut également être utilisé de manière astucieuse, par exemple en comparant plusieurs attributs numériques de catégories concurrentes et en renvoyant l'alias ou la description du champ pour visualiser la prédominance.

Par exemple, dans un ensemble de données électorales, vous pouvez avoir des colonnes qui représentent le nombre total de votes pour des candidats individuels, mais aucune colonne qui indique le vainqueur (car aucun n'a encore été déclaré). Vous pouvez utiliser Decode pour renvoyer le leader actuel d'une élection lors de la mise à jour des résultats :
var votesParParti = [
  $feature.RN,
  $feature.REP,
  $feature.LREM,
  $feature.LFI,
  $feature.EELV,
  $feature.SOC,
];

// Récupère le score le plus élevé,
// trouve la correspondance avec le parti correspondant
// et retourne le nom complet du parti leader
var leader = Decode(Max(votesParParti),
  $feature.RN, "Rassemblement national",
  $feature.REP, "Les républicains",
  $feature.LREM, "La république en marche",
  $feature.LFI, "La France insoumise",
  $feature.EELV, "Europe écologie les verts",
  $feature.SOC, "Socialistes",
  "Autre"
);

return leader;

4. La fonction "DefaultValue"

DefaultValue renvoie une valeur si une valeur vide (null ou '') est détectée. Les utilisateurs souhaitent généralement afficher le texte par défaut au cas où une valeur n'existe pas. De nombreuses personnes utilisent une combinaison de IIf et IsEmpty pour tenir compte des valeurs vides.
// retourne une valeur par défaut de 0 si le champ de données est vide
IIF(!IsEmpty($feature.monchamp), $feature.monchamp, 0);
Cela fonctionne, mais la fonction DefaultValue fait tout le travail pour vous. Tout ce que vous avez à faire est de spécifier la valeur que vous souhaitez vérifier, et la valeur par défaut à retourner si elle est vide.

L'équivalent de l'expression précédente est plus facile à lire :
// retourne une valeur par défaut de 0 si le champ de données est vide
DefaultValue($feature.monchamp, 0);
Dans les fenêtres contextuelles, il peut être utile d'afficher un message indiquant qu'aucune donnée n'est disponible afin que l'utilisateur sache que les données sont collectées dans la couche, mais ne sont pas disponibles pour l'entité sélectionnée.
// retourne "Aucune donnée disponible" si le champ etat est vide
DefaultValue($feature.etat, "Aucune donnée disponible");
Je suis récemment tombé sur l'expression suivante qui aurait pu être réduite de moitié si l'auteur de l'expression avait été utilisée DefaultValue. Vous noterez comment la valeur de retour dans le if-else sont presque identiques. La seule différence est d'afficher la valeur d'un attribut ou d'un autre si le premier est vide.
var ValeurPourcent = Round(($feature.Visiteurs/$feature.TotalVisiteurs)*100, 2) + "%";
var TypeParc = Lower($feature.TYPE_PARC);
var NomParc = $feature.NOM_PARC;
var CodeParc = $feature.CODE_PARC;

if(IsEmpty(NomParc)){
  return {
    text: "{CodeParc} est un parc de type " + TypeParc + " qui a eu " +
    "{$feature.Visiteurs} visiteurs en 2022. " +
    "cela représente " + ValeurPourcent + " des visites" +
    "de tous les parcs en 2022."
  }
} else {
  return {
    text: "{NomParc} est un parc de type " + TypeParc + " qui a eu " +
    "{$feature.Visiteurs} visiteurs en 2022. " +
    "Cela représente " + ValeurPourcent + " des visites" +
    "de tous les parcs en 2022."
  }
}
Il s'agit de l'expression mise à jour tirant parti DefaultValue de la variable NomParc, ce qui entraîne moins de duplication de texte dans l'expression. J'utilise également Text pour nettoyer le formatage des nombres et rendre les valeurs sensibles aux paramètres régionaux.
var ValeurPourcent = Round(($feature.Visiteurs/$feature.TotalVisiteurs)*100, 2) + "%";
var TypeParc = Lower($feature.TYPE_PARC);
var NomParc = DefaultValue($feature.NOM_PARC, $feature.CODE_PARC);

return {
  text : NomParc + " est un parc de type " + TypeParc + " qui a eu " +
    Text($feature.Visiteurs, "#,###") +
    " visiteurs en 2022. Cela représente " +
    ValeurPourcent + " des visites de tous les parcs en 2022."
};


5. La fonction "Number"

La fonction Number convertit une valeur d'entrée de n'importe quel type en un nombre. Les gens demandent souvent une fonction pour renvoyer le nombre de millisecondes depuis le 1er janvier 1970 similaire à la méthode Date.getTime() de JavaScript. Cette fonction existe déjà ! Number le fera si vous transmettez une valeur de date en tant que paramètre à la fonction.
Number(Date())
// retourne la valeur d'époque
Il est également courant que les utilisateurs stockent des valeurs binaires en tant qu'attribut d'entité. Certains attributs sont vrais ou faux, donc la valeur sera stockée sous la forme 0 (false) ou 1 (true).

J'ai parfois vu des expressions faire ce qui suit lors du calcul d'une valeur dans les workflows de mise à jour :
IIF($feature.etat == "terminé", 1, 0);
// retourne 1 si vrai, 0 si faux
Vous pouvez également tirer parti de Number pour renvoyer un 1 ou un 0 lors de l'évaluation d'une expression booléenne.
Number($feature.etat == "terminé");
// retourne 1 si vrai, 0 si faux
Number peut également être utile si vous avez besoin d'analyser une valeur à partir d'un texte et de la convertir en nombre pour un calcul ou une visualisation numérique.
Number("38.7%", "#.#%");
// retourne 0.387

Number("1,000 personnes", "# personnes");
// retourne 1000


Et les plus sous-utilisées des fonctions, ce sont généralement vos propres fonctions !

Si vous vous retrouvez à écrire du code en double dans une expression, vous tirerez probablement profit de l'écriture d'une fonction personnalisée. Les fonctions personnalisées vous permettent d'écrire une fois du code qui, sinon, devrait être écrit plusieurs fois dans votre expression, et de leurs donner un nom pour pouvoir les appeler. Cela réduit considérablement les erreurs qui se glissent avec le copier/coller et peut réduire considérablement la taille d'une expression.


Conclusion

Comme avec tout autre langage de scripting ou d'expression, il existe plusieurs façons de résoudre une tâche ou d'arriver à un résultat correct. Cependant, les fonctions décrites dans cet article (IIf, Text, Decode, DefaultValue, Number) peuvent simplifier vos expressions, améliorer leur lisibilité et réduire les bugs. Définir vos propres fonctions peut également vous faire gagner du temps, réduire la longueur de vos expressions et réduire les bogues introduits dans les opérations de copier/coller.

Partager cet article:

Rejoindre la discussion

    Les commentaires à propos de cet article: