Select to view content in your preferred language

Building a Data Expression: Populating Fields & Attributes

3808
4
04-03-2023 12:04 PM
jcarlson
MVP Esteemed Contributor
8 4 3,808

If you know me, you know I love a good Data Expression in a Dashboard.

But if you know Data Expressions, you know they can be a bit tedious to build, defining all those fields and feature attributes. I wanted to share a couple of shortcuts you can use in your next Data Expression.

Populating Fields: Schema and Splice

Maybe you've been in this situation: you want to use a Data Expression to create one or two new fields, but the output needs to include a lot of the fields from the input FeatureSet. You've got better things to do than re-write the schema!

If you haven't tried it out yet, take a look at the Schema function, and what it returns:

jcarlson_0-1680545920558.png

Let's look closer at the fields object:

jcarlson_1-1680545969940.png

Isn't that what we want? Yes, it is! But we need to add our new fields to it. Enter the Splice function, which takes two arrays and just smooshes them together. Next time you run into this situation, try something like this:

var fields = Schema(fs)['fields'];

var new_fields = [
    {name: 'next_due', type: 'esriFieldTypeDate'},
    {name: 'window_start', type: 'esriFieldTypeDate'},
    {name: 'overdue', type: 'esriFieldTypeString'},
    {name: 'current_ok', type: 'esriFieldTypeString'},
]

var out_dict = {
    fields: Splice(fields, new_fields),
    geometryType: '',
    features: []
}

You've still got to define those new fields, there's no way around it. But you can just copy the schema for the others, the ones I call "pass-through" attributes. In just a few lines, I can tee up my full schema for adding features. If my input FeatureSet had, say, 30 fields I wanted included, it would take twice as many lines just to define those, to say nothing of the new ones.

Bonus: using Schema grabs the alias and domains for your fields, too!

 

Populating Attributes

So now we're in our for (var f in fs) { ... } loop, populating the output features array. If you've got lots of attributes in the output, this can get pretty tedious, too. Unfortunately, a Feature object is immutable, so we can't just add or change attributes to the features.

Consider this, though. Look what happens when you loop through a Feature:

jcarlson_2-1680547065557.png

It's actually looping through the attributes dictionary keys! So check this out: we create a new dictionary, then push the feature attributes into it.

* record screech sound effect*

What about dates, though? If you've read this far and you're still interested, you probably know that Date values tend to break Data Expressions. You need to do Number(date_value) for it to work. If you iterate over your attributes, can you still do that? Yes! We just use the function TypeOf to check if it's a date, then adjust.

var attrs = {}

for (var attr in f) {
    attrs[attr] = Iif(TypeOf(f[attr]) == 'Date', Number(f[attr]), f[attr])
}

jcarlson_4-1680547710690.png

But, like the Schema function, this only helps us with the attributes that already existed in incoming FeatureSet, not our new fields. There's no Splice for dictionaries, but since we can create new values simply by calling dictionary['new_key'] = new_value, it's pretty simple:

attrs['some_new_field'] = some_value
// and so on

So, back to the real example I'm working with, I've got my 4 new fields I want to add to the output, and half a dozen "pass-through" attributes. I will actually define those new fields at the same time as creating the dictionary, then do the attribute loop.

Remember, this is all in my for (var f in fs) loop, so at the end, I'm pushing the feature into the features array in my output dict.

    // 4 new fields in dict
    var attrs = {
        next_due: Number(next_due),
        overdue: Iif(lr < prev_due && !IsEmpty(lr), 'OVERDUE', ''),
        current_ok: Iif(lr > window_start && !IsEmpty(lr), 'OK', ''),
        window_start: Number(window_start)
    }

    // add pass-through fields
    for (var attr in f) {
        attrs[attr] = Iif(TypeOf(f[attr]) == 'Date', Number(f[attr]), f[attr])
    }
    
    // Add feature to output dict
    Push(out_dict['features'], {attributes: attrs})

Again, I managed this all in just a handful of lines, where this could ordinarily take a great deal more if I have a lot of fields from the input FeatureSet. As the number of "pass-through" fields increases the benefits do, too, as this method can apply to any number of input fields without any changes to the code.

Bonus: the attrs dictionary is not a feature, so its values are mutable. Suppose I had a field in the input that I wanted to change slightly. As long as I do it before the Push function, I can, and without needing to creating any new variables.

attrs['establishment_name'] = Proper(attrs['establishment_name'])

 I hope some of this helps you out the next time you're working on a Data Expression!

4 Comments
About the Author
I'm a GIS Analyst for Kendall County, IL. When I'm not on the clock, you can usually find me contributing to OpenStreetMap, knitting, or nattering on to my family about any and all of the above.