Issue with IIF statement in arcade for valueExpression

1615
10
Jump to solution
01-29-2021 01:19 PM
RabinSubedi
New Contributor III

Hello Everyone,

I have two arrays pavementID and pavementCondition inside treatmentPlan object. I need to check if pavementID containes the Pavement ID of a layer (field: PvtID) or not. If yes, I have to use value of pavementCondition that corresponds with the index of PvtID in pavementID. If not, I have to use existing value of the layer (field: PCR_NBR). I wrote an arcade expression like this, but it didn't worked:

Can anyone help me with this?

Thank You.

var arcadeExp = "IIF(" + treatmentPlan.pavementIDs.includes(+"$feature.PvtID" + ) + "," + treatmentPlan.pavementCondition[treatmentPlan.pavementIDs.indexOf(+"$feature.PvtID" + )] + ",$feature.PCR_NBR )";
                  var majorRoadRenderer = {
                    type: "class-breaks",
                    valueExpression: "(" + arcadeExp + ")",
                    classBreakInfos: [{
                        minValue: 86, 
                        maxValue: 100, 
                        symbol: {
                          color: "#005ce6",
                          type: "simple-line",
                          style: "solid",
                          width: "3px",
                        },
                      }, {
                        minValue: 76,
                        maxValue: 85, 
                        symbol: {
                          color: "#38a800",
                          type: "simple-line",
                          style: "solid",
                          width: "3px",
                        },
                      },
                      {
                        minValue: 66, 
                        maxValue: 75, 
                        symbol: {
                          color: "#fc921f",
                          type: "simple-line",
                          style: "solid",
                          width: "3px",
                        },
                      }, {
                        minValue: 56, 
                        maxValue: 65, 
                        symbol: {
                          color: "#e60000",
                          type: "simple-line",
                          style: "solid",
                          width: "3px",
                        },
                      }, {
                        minValue: 0, 
                        maxValue: 55, 
                        symbol: {
                          color: "#1a1a1a",
                          type: "simple-line",
                          style: "solid",
                          width: "3px",
                        },
                      }
                    ]
                  };

 

 

0 Kudos
1 Solution

Accepted Solutions
BlakeTerhune
MVP Regular Contributor

I don't think JSON.parse() is available in Arcade so your assignment for planObj isn't needed. And just for testing to simplify everything, use the original JS object. What happens if you change it to

var arcadeExp = `
    var planObj = ${JSON.stringify(treatmentPlan.pavementIDs)};
    var pvtID = 100;
    return planObj[pvtID]`;
console.log(arcadeExp);

Then add a console.log(arcadeExp) to make sure the Arcade expression is correct after JS inserts everything. You can verify the logged expression in the Arcade Playground.

View solution in original post

10 Replies
DavidPike
MVP Frequent Contributor

Just a suggestion, but from most python stuff I've come across, you really just need to pass the expression in as string.

Perhaps:

var arcadeExp = "IIF(treatmentPlan.pavementIDs.includes($feature.PvtID), treatmentPlan.pavementCondition[treatmentPlan.pavementIDs.indexOf($feature.PvtID)], $feature.PCR_NBR)"
0 Kudos
BlakeTerhune
MVP Regular Contributor

You're mixing a lot of stuff up in that one assignment for arcadeExp. There are syntax issues the way you have it now. Try splitting it up like this to simplify the logic. If this app is meant for modern browsers (ES6) use template literals to make it more readable.

var arcadeExp = `var indexPvtID = ${treatmentPlan.pavementIDs}.indexOf($feature.PvtID);
    IIF(
        indexPvtID >= 0,
        ${treatmentPlan.pavementCondition}[indexPvtID],
        $feature.PCR_NBR
    )`;

It seems like there's a potential weak point with grabbing the value of pavementCondition based on index from pavementIDs. You'd be better off making pavementCondition an object where the keys are the values from pavementIDs and the value of each is whatever each value is in pavementCondition.

pavementCondition = {
  pavementID1: pavementConditionValue1,
  pavementID2: pavementConditionValue2,
  pavementID3: pavementConditionValue3,
  pavementID4: pavementConditionValue4,
  pavementID5: pavementConditionValue5,
  // etc...
}

Then you could

var arcadeExp = `var pavementCondition = ${treatmentPlan.pavementCondition};
    IIF(
        HasKey(pavementCondition, $feature.PvtID),
        pavementCondition[$feature.PvtID],
        $feature.PCR_NBR
    )`;

I'm not an Arcade expert and haven't tested this so I might be missing something obvious but hopefully you can make it work.

RabinSubedi
New Contributor III

Thanks @BlakeTerhune,. I initially tried to avoid arcade and use the first method that you mentioned, but I didn't know how to pass variables from code to expression like this: 

${treatmentPlan.pavementCondition}
I will try using this method. Thanks a lot!

0 Kudos
RabinSubedi
New Contributor III

@BlakeTerhune  I tried this method, but it doesn't allow me to pass array or object as a whole into the arcade expression, although I can pass a single value. I tried using JSON.stringify, it passes the entire content inside but it doesn't returns the value for given index (for eg. pvtID=100 here). I tried using JSON.parse inside the template literal to revert it back to object, but it throws same error as before.

var pvtInPlan = treatmentPlan.pavementIDs;
                  var pcrAfterPlan = treatmentPlan.pavementCondition;
                  var yearlyPlan = {};
                  pvtInPlan.forEach((key, i) => yearlyPlan[key] = pcrAfterPlan[i]);
var arcadeExp = `
									var plan = ${JSON.stringify(yearlyPlan)};
									var planObj = JSON.parse(plan);
									var pvtID = 100;
									return planObj[pvtID]`;  //I am trying to pass a single value instead of IIF statement just to test if object is working or not

 

0 Kudos
BlakeTerhune
MVP Regular Contributor

Does it work without any of the JSON manipulation? I think Arcade should be able to use the JavaScript object natively.

0 Kudos
RabinSubedi
New Contributor III

No it doesn't. For example, if I use the following expression, it gives me an error:

var arcadeExp = `
									var plan = ${yearlyPlan};
									var pvtID = 100;
									return plan[pvtID]`;

But if I pass just one value, it works:

var arcadeExp = `
									var plan = ${yearlyPlan[100]};
									return plan`;

 

This is the error I get while passing object/array:

<a class='gotoLine' href='#"[esri.views.2d.layers.FeatureLayerView2D]", [object Object'>"[esri.views.2d.layers.FeatureLayerView2D]", [object Object</a> {
  constructor: function e(b,g,d){var c=a.call(this,b,g,d)||this;return c instanceof
e?c:new e(b,g,d)},
  details: <a class='gotoLine' href='#object Object'>object Object</a> {
    stack: "ra@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:403:347
sa@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:405:349
Q@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:405:421
M@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:406:41
E@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:409:336
X@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:181
db@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:416:267
X@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:181
Ha@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:417:470
X@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:181
Xa@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:419:9
Ga@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:420:156
X@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:181
eb@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:421:200
X@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:181
oa@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:423:69
da@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:407:84
Za@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:426:362
$a@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:426:497
Ca@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:432:230
Ra@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:426:157
ab@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:433:447
cb@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:436:265
Ra@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:426:140
esri/arcade/lib/esprima/&lt;/v.parse@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:462:35
esri/arcade/parser/&lt;/a.parseScript@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:381:192
y@https://js.arcgis.com/4.17/esri/support/arcadeUtils.js:4:499
esri/support/arcadeOnDemand/&lt;/f&lt;/a.create/&lt;/&lt;@https://js.arcgis.com/4.17/esri/views/2d/layers/features/Pipeline.js:590:85
d@https://js.arcgis.com/4.17/dojo/dojo-lite.js:47:190
c/&lt;@https://js.arcgis.com/4.17/dojo/dojo-lite.js:45:492
g@https://js.arcgis.com/4.17/dojo/dojo-lite.js:45:275
"
  },
  message: "Line 2: Unexpected identifier",
  name: "Error",
  toJSON: function(){if(null!=this.details)try{return{name:this.name,message:this.message,details:JSON.parse(JSON.stringify(this.details,function(a,b){if(b&&"object"===typeof b&&"function"===typeof b.toJSON)return b;try{return l.clone(b)}catch(d){return"<a class='gotoLine' href='#object'>object</a>"}}))}}catch(n){throw h.getLogger("esri.core.Error").error(n),n;}return{name:this.name,message:this.message,details:this.details}},
  toString: function(){return"<a class='gotoLine' href='#"+this.name+"'>"+this.name+"</a>: "+this.message},
  type: "error"
}]
0 Kudos
BlakeTerhune
MVP Regular Contributor

I don't think JSON.parse() is available in Arcade so your assignment for planObj isn't needed. And just for testing to simplify everything, use the original JS object. What happens if you change it to

var arcadeExp = `
    var planObj = ${JSON.stringify(treatmentPlan.pavementIDs)};
    var pvtID = 100;
    return planObj[pvtID]`;
console.log(arcadeExp);

Then add a console.log(arcadeExp) to make sure the Arcade expression is correct after JS inserts everything. You can verify the logged expression in the Arcade Playground.

RabinSubedi
New Contributor III

That worked! I used the following code:

 

var arcadeExp = `
									var planID = ${JSON.stringify(treatmentPlan.pavementIDs)};
									var planPCR = ${JSON.stringify(treatmentPlan.pavementCondition)};
									var index = IndexOf(planID, $feature.PvtID);
									return IIF(index>=0, planPCR[index],$feature.PCR_NBR)`;

 

I noticed that, if we try to pass object or dictionary, it won't work. Also if we try to create dictionary inside arcade using arrays from stringify, it gives the same error.

Thank you very much for your help @BlakeTerhune. You are awesome!

BlakeTerhune
MVP Regular Contributor

Glad you found a solution and appreciate you posting it. I didn't realize that putting an object in the JS template literal that it comes out as [object Object] so that JSON.stringify() was a great find on your part!