Mutate a dictionary or map.

As an example, in Javascript I can do this:

var test = {a:1,b:2}
test.a = 'changed'
console.log(test)

and it will output this:

{a:"changed",b:2}

Because I mutated the value in key 'a' of the test variable.

How do I do this in an appian expression?

Trying this:

a!localVariables(
  local!test: {a:1,b:2},
  local!test["a"]: "changed",
  local!test
)

Gives me this error:

Expression evaluation error at function 'localvariables': A variable is incorrectly defined. Parameter: 2. Expected syntax: localvariables(local!a, ..., expr) or localvariables(local!a:10, ..., expr)

So it seems you can't changed a key of a variable in an expression. Is there another way to do this? It seems like such an elementary operation...

  Discussion posts and replies are publicly visible

Parents
  • +1
    Certified Lead Developer

    Your main mistake here is assuming Appian expressions are like lines of code that execute in order.  Instead they should be thought of as a single code statement that will be flattened and evaluated when run.

    Thus the technical answer to your question, "how can I make local!test.a == 'changed'", is to declare local!test.a as "changed" in the first place.  This might seem like "the snarky answer" but it's also true.  Diclaimer: when we start working in interfaces where users interact, this all changes.

    The *real* answer to what you're getting at, though, is probably this: when you want to iteratively transform data in local variables, we declare subsequent local variables to handle iterative values.  Assume you're fed your local!test value up front.  Now you can call "a!update()" on it, feed in the property you want to swap out to a different value, then feed the different value.  If you do this as the expression's output, you'll see a readout of the altered dictionary.  If you want to keep that for later use, though, you can store that new value in 'local!test2' (etc).

  • Thank you!

    But no I didn't make that assumption. I understood that a lot of code in Appian ran in parallel, but that if a line depends on another line then execution order is enforced due to the dependency.

    I just had no other way of expressing my need but to show it in a language I'm familiar with but that unfortunately doesn't do the same type of parallel execution.

    I was looking for something like that function you mentioned, but I had no idea what to search for and I don't think Appian's documentation is very easy to search though. Now that I'm aware of that function though, my code example can be completed like so:

    a!localVariables(
      local!test: {a:1,b:2},
      local!test2: a!update(local!test, "a","changed"),
      local!test2
    )

    (There's no Appian language option for code inserted in this forum, so sadly I have to format everything as just "text")

    So, based on how a!update works, and my lack of evidence showing otherwise, it seems that Appian variables are immutable and you can only declare a new modified variable, but not change the original. Correct?

    So if I had a large map or dictionary and I wanted to change all the keys, I'd have to make a new map for every individual key/value change? That seems like it could waste a lot of memory / time. You can't edit an existing map in place?

    Same with a!append(), it returns a new array instead of just adding to the old one. If I try to assemble a large array, the only way I can think of doing it is like so:

    a!localVariables(
      local!test: {1,2,3,4,5,6,7,8,9,10},
      local!test2: reduce(
        fn!append
        ,{}
        ,a!forEach(items:local!test,expression:local!test)
      )
      ,local!test3: reduce(
        fn!append
        ,{}
        ,a!forEach(items:local!test2,expression:local!test2)
      )
      ,local!test3
    )
    


    Which makes an array of 10,000 integers, but it takes almost one second, about 800ms. The same operation in JS takes 10ms, likely because it's just appending to the same array, at each step, instead of making 10 new arrays, and then 100 new arrays, and then 10,000 new arrays.

    Here's the closest thing I could come up with in JS:

    console.time('t')
    var test = [1,2,3,4,5,6,7,8,9,10]
    ,test2 = test.map(()=>test).reduce((acc, curr, currI, arr) => acc.concat(curr), [])
    ,test3 = test2.map(()=>test2).reduce((acc, curr, currI, arr) => acc.concat(curr), [])
    console.log(test3)
    console.timeEnd('t') // ~10ms


    I know immutability has the benefit of thread-safety which allows sharing the objects between threads, which I suppose helps to allow Appian to be designed to run things in parallel more easily, but mutable objects are often very practical. I don't build a whole new car when I want to change the headlights in a particular car, it makes more sense to just change the headlights on the existing car, but I might buy a whole new mug if I want one with a different handle because that's not prohibitively expensive. A mutable mug might not make sense, but a mutable car is probably a practical design choice.

    So does Appian really not have mutable objects? They would be much more efficient when trying to transform large datasets imported from external APIs.

  • 0
    Certified Lead Developer
    in reply to aj2000
    (There's no Appian language option for code inserted in this forum, so sadly I have to format everything as just "text")

    For what it's worth, this is a long-standing complaint of mine around here - i've brought it up with a few different folks in charge of Community so far, but despite the suggestion always being met with rather enthusiastic agreement, it's never seemed to actually get addressed.  I just choose "Java" as its syntax highlighting comes ~close-ish to SAIL in most uses.

  • 0
    Certified Lead Developer
    in reply to aj2000
    They would be much more efficient when trying to transform large datasets imported from external APIs.

    I'm not sure I really understand your general use case, at least up to your mention of large datasets from external APIs.

    I've been doing this >10 years now and honestly I've never had much of an issue with the immutability of Appian data objects - it seems to me almost as if you're starting with a conclusion then working backwards to provide examples that meet it.  For instance if you want "array of 10,000 integers" you could simply do enumerate(10000) - in my dev environment that takes 1ms.

    For large datasets imported from external APIs i believe the "best practice" recommendation is to throw that data into a process instance and handle it there.  In successive process nodes you can change any nature of any particiular item in an array individually or in groups, in a specific order.  To be honest though, in all but the biggest and most unruly data sets, it's probably sufficient to just transmute incoming data using expression functionality - it just takes some practice at using Appian's functionality to do it.

Reply
  • 0
    Certified Lead Developer
    in reply to aj2000
    They would be much more efficient when trying to transform large datasets imported from external APIs.

    I'm not sure I really understand your general use case, at least up to your mention of large datasets from external APIs.

    I've been doing this >10 years now and honestly I've never had much of an issue with the immutability of Appian data objects - it seems to me almost as if you're starting with a conclusion then working backwards to provide examples that meet it.  For instance if you want "array of 10,000 integers" you could simply do enumerate(10000) - in my dev environment that takes 1ms.

    For large datasets imported from external APIs i believe the "best practice" recommendation is to throw that data into a process instance and handle it there.  In successive process nodes you can change any nature of any particiular item in an array individually or in groups, in a specific order.  To be honest though, in all but the biggest and most unruly data sets, it's probably sufficient to just transmute incoming data using expression functionality - it just takes some practice at using Appian's functionality to do it.

Children
No Data