Attribute Rule Calculation: Count the occurrences of a value in a number of fields

2649
7
Jump to solution
04-07-2022 04:44 AM
ToddLusk
New Contributor III

I'm attempting to create an Attribute Rule Calculation that counts the number of occurrences of a given value ("x") in a number of fields in a dataset.  I want to update a different field ("IssuesCount") in the said dataset with that number.  In total, there are 15 fields that I need to check.

My thought was that I would start with a list of fields names that I want to check then dynamically generate the field names, check to see if they contained "x", then count up the occurrences.  But I'm struggling to dynamically generate the field names from the list.

Here's the list of fields :

 

var fieldList = ['IssueAdjacentProperty', 'IssueCultivation',
'IssueFoodPlots', 'IssueHaying', 'IssueTimberHarvestCutting',
'IssueBurning', 'IssueNewExpandedTrail', 'IssueNewExpandedRoad',
'IssueParkedEquipment', 'IssueNewBuildings', 'IssueGrazingLivestock',
'IssueCommercialSeed', 'IssueUtilities', 'IssueMining', 'IssueImpervious',
'IssueHydrology', 'IssueUnauthorizedEasement', 'IssueWaste', 'IssueMowing',
'IssueOther', 'IssueExpandedYard', 'IssueFencing', 'IssueErosion',
'IssueStormwater', 'IssueYardWaste']

 

 

And here's me trying to dynamically generate the field names to check the values:

 

 

// Dynamically generate field name
for (var f in fieldList){
  // Doesn't work as it returns the literal string of '$feature.0'
  var fieldName = "$feature." + f​

  // Return the value in the field.
  // This part doesn't work because 'fieldName' equals
  // '$feature.0' at this point and Arcade is looking
  // for a field called 'fieldName' here.
  var checkValue = $feature.fieldName

  // Then do something like loop through fields checking for 'x'
  // (unless there is a more efficient way to do that too)
  var counter = 0
  if (checkValue == 'x') {
    counter++;
  }
}

// Return the count
return counter;

 

 

 I think once I get this field name piece figured out I have, I know what to do on the rest of the Attribute Rule side of things.

Thanks for any help!

Todd Lusk, Senior GIS Specialist
Dakota County, MN

0 Kudos
1 Solution

Accepted Solutions
jcarlson
MVP Esteemed Contributor

A few different things.

1. In a for loop with an array, your variable is the index. Use parentarray[index] to access the actual contents of that item.

2. You're adding the text string "$feature." to your fieldname variable, which you are then accessing with $feature.fieldname. Written out, that would be "$feature.$feature.", plus whatever your field name is.

Accessing fields with dot notation only works with literal field names, and then only if the field name has no invalid characters.

jcarlson_0-1649334876312.png

Instead, try using bracket notation. Your field name in this situation is just a string, so you can pipe in any variable.

jcarlson_1-1649334977292.png

3. Establish your count outside of the loop. Otherwise you're going to re-set it to 0 every time it goes to the next field in the list.

There are lots of ways to do the counting bit, but looking at the fields in a for loop seems fine. You probably don't need so many variables, though.

var fieldList = ['your', 'field', 'names']

var total = 0

for (var f in fieldList){
    if ($feature[fieldList[f] == 'x'){
        total += 1
    }
}

return total

 

- Josh Carlson
Kendall County GIS

View solution in original post

7 Replies
jcarlson
MVP Esteemed Contributor

A few different things.

1. In a for loop with an array, your variable is the index. Use parentarray[index] to access the actual contents of that item.

2. You're adding the text string "$feature." to your fieldname variable, which you are then accessing with $feature.fieldname. Written out, that would be "$feature.$feature.", plus whatever your field name is.

Accessing fields with dot notation only works with literal field names, and then only if the field name has no invalid characters.

jcarlson_0-1649334876312.png

Instead, try using bracket notation. Your field name in this situation is just a string, so you can pipe in any variable.

jcarlson_1-1649334977292.png

3. Establish your count outside of the loop. Otherwise you're going to re-set it to 0 every time it goes to the next field in the list.

There are lots of ways to do the counting bit, but looking at the fields in a for loop seems fine. You probably don't need so many variables, though.

var fieldList = ['your', 'field', 'names']

var total = 0

for (var f in fieldList){
    if ($feature[fieldList[f] == 'x'){
        total += 1
    }
}

return total

 

- Josh Carlson
Kendall County GIS
ToddLusk
New Contributor III

Thanks, Josh!  This seems to be the way my brain was thinking about it but clearly didn't have the syntax quite right.

JohannesLindner
MVP Frequent Contributor

There are multiple ways to use for loops:

var arry = [1, 2, 3]

// The "normal" way
// we define start value, end value, and increment of the loop variable
for(var i = 0; i < Count(array); i++) {
    Console(array[i])
}

// The "for in" way
// This is a shorter form of the "normal" approach
// the loop variable is the index of the array
for(var i in array) {
    Console(array[i])
}

// the "for in" way on a FeatureSet
// when you use "for in" on a feature set, the loop variable is not the index
// of the feature set, but the actual feature.
var fs = FeatureSetByName(...)
for(var f in fs) {
    Console(f.Field)
}

 You tried to mix the second and third way. f is the index of fieldList, not the value at that index.

 

Easiest way to do this is probably this:

// get the _values_
var value_list = [
    $feature.IssueAdjacentProperty,
    $feature.IssueCultivation,
    $feature.IssueFoodPlots, 
    $feature.IssueHaying, 
    $feature.IssueTimberHarvestCutting,
// ...
]

// define a filter function
function equals_search_value(val) { return val == "x" }

// filter the value array and return the count
return Count(Filter(value_list, equals_search_value))

 


Have a great day!
Johannes
ToddLusk
New Contributor III

Thanks, Johannes!  While my brain doesn't quite comprehend your method as well as it does Josh's above, I can see how this method works too.

ToddLusk
New Contributor III

I'm getting an "Error on line 9.  String type expected" message when I try this method.  Is trying to use "$feature" on a standalone table an issue (i.e., does the data source have to be a feature layer)?  I tried whittling my list down to just one field to be sure that wasn't the problem.

0 Kudos
JohannesLindner
MVP Frequent Contributor

Hmmm... Sadly, I have no idea what could cause that error. I have only tested my expression in the Arcade Playground, maybe there's some hickups when you involve actual data. You should probably stick to Josh's answer.

JohannesLindner_0-1649404423989.png

 

Is trying to use "$feature" on a standalone table an issue (i.e., does the data source have to be a feature layer)?

No, $feature and FeatureSetBy*() work on non-feature-class tables, too.


Have a great day!
Johannes
0 Kudos
ABishop
MVP Regular Contributor

Hello Todd,

I have done something similar with counts, but instead of counting data in fields I have created a script that uses the "FieldStatisticsToTable" geoprocessing tool.  What it does is, it counts the stats in several tables using my unique ID then puts the count into a stand alone table for each.  Once all the stand alone tables are created, I have one master table where I append the counts for one report.  A suggestion on how to extract only the ("x") in a number of fields is to set up a definition query in your dataset to only display those records OR you could use "FeatureToFeatureClass" geoprocessing tool to create another table (preferably in a file geodatabase) from the default dataset and use it to run the field stats.  If you need a new field "IssueCount" to be visible, you can add it as a field to your new table that is created with the featuretofeatureclass tool and append the statistic counts to that field.

Amanda Bishop, GISP
0 Kudos