7

I have a For_Each loop in an Azure Logic App that calls another, nested, Logic App. The result from each iteration of the nested Logic Apps is a JSON object that contains an array of strings, like this:

{
 "Results": ["string a", "string b"]
}

So the output from my For_Each loop in the parent Logic App looks like this:

[
 {"Results": ["string a", "string b"]},
 {"Results": ["string c", "string d"]}
]

I want to put all these strings into a single flat list that I can pass to another action.

How can I do this? Is it possible using the workflow definition language and built-in functions, or do I need to use an external function (in a service, or an Azure Function)?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Jude Fisher
  • 11,138
  • 7
  • 48
  • 91

5 Answers5

8

There's a simpler solution, working with Array Variables. At the top level, outside the For Each loop, declare a variable with an InitializeVariable action:

"Initialize_Items_variable": {
    "inputs": {
        "variables": [
            {
                "name": "Items",
                "type": "Array",
                "value": []
            }
        ]
    },
    "runAfter": {},
    "type": "InitializeVariable"
}

Inside the For Each, use a AppendToArrayVariable action. You can append the Response object of the Nested Logic App you just called.

"Append_to_Items_variable": {
    "inputs": {
        "name": "Items",
        "value": "@body('Nested_Logic_App_Response')"
    },
    "runAfter": {
    },
    "type": "AppendToArrayVariable"
}

Hope it helps.

Grazi Bonizi
  • 96
  • 1
  • 3
  • I wish there was just an `append()` function for arrays in the workflow language instead of creating a whole action to do it. – ADJenks Mar 10 '23 at 22:33
1

Picking up on @DerekLi's useful comment above, it seems this is not possible at the time of writing with Logic Apps schema version 2016-06-01.

One of the great strengths of Logic Apps is the ability to leverage the power of Azure Functions to solve problems like this that can't (yet) be solved in the schema language.

Re-writing the array is trivial in c# within a function:

using System.Net;

public class Result
{
    public List<string> Results {get; set;}
}

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    var inputs = await req.Content.ReadAsAsync<List<Result>>();
    var outputs = new List<string>();

    foreach(var item in inputs)
    {
        log.Info(item.Results.ToString());
        outputs.AddRange(item.Results.Where(x => !string.IsNullOrEmpty(x)));
    }

    return req.CreateResponse(HttpStatusCode.OK, outputs);
}

And this function can then be passed the result of the For_Each loop:

"MyFunction": {
    "inputs": {
                "body": "@body('Parse_JSON')",
                "function": {
                    "id": "/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/Microsoft.Web/sites/{function-app-name}/functions/{function-name}"
                },
                "method": "POST"
            },
            "runAfter": {
                "For_each": [
                    "Succeeded"
                ]
            },
            "type": "Function"
}
Jude Fisher
  • 11,138
  • 7
  • 48
  • 91
1

There is also a way to do it using the workflow definition language. (https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-workflow-definition-language).

Using the fonctions string and replace you can work on your json as a string rather than on objects.

Here is a Flat_List action that follows a Parse_JSON action with your data:

Your data:

[
 {"Results": ["string a", "string b"]},
 {"Results": ["string c", "string d"]}
]

Flat_List component:

 "Flat_List": {
            "inputs": "@replace(replace(replace(string(body('Parse_JSON')),']},{\"Results\":[',','),'}]','}'),'[{','{')",
            "runAfter": {
                "Parse_JSON": [
                    "Succeeded"
                ]
            },
            "type": "Compose"
        },

What happens here? First we use string that takes your json data and gives:

[{"Results":["string a", "string b"]},{"Results":["string c", "string d"]}]

We replace all the ]},{"Results":[ by ,.

We replace all the }] by }.

We replace all the [{ by {.

We get the string {"Results":["string a","string b","string c","string d"]}

Then you are free to parse it back to json with:

"Parse_JSON_2": {
                "inputs": {
                    "content": "@outputs('Flat_List')",
                    "schema": {
                        "properties": {
                            "Results": {
                                "items": {
                                    "type": "string"
                                },
                                "type": "array"
                            }
                        },
                        "type": "object"
                    }
                },
                "runAfter": {
                    "Flat_List": [
                        "Succeeded"
                    ]
                },
                "type": "ParseJson"
            }

You can see it as a proof of concept as the Azure Function may be easier to re-read later but there may be many reason not to want to instantiate a new Azure Function while you can do the job in Logic App.

Feel free to ask for more details if needed :)

Julien B.
  • 66
  • 3
1

This technique works pretty well, and only uses run-of-the-mill Logic App actions:

    1. start with declaring an empty array variable (action Variable: Initialise variable)
    2. iterate through your items (action Control: For each), e.g. the resultset from a previous action
    • in each iteration, first compose the JSON fragment you need (action Data Operations: Compose)
    • then append the output of your Compose action to the array (action: Variable: Append to array variable)
    3. then, outside the loop, join the elements of the array (action Data Operations: Join)
    4. do what you need with the output of the Join action, e.g. send as response payload (action Request: Response)

This is what it looks like in the end:

Logic App Designer screenshot

nicdaniau
  • 3,356
  • 1
  • 13
  • 10
0

You can use @body(nestedLogicApp) outside of the for-each loop to access all the nested Logic Apps' response in an array.

Derek Li
  • 3,089
  • 2
  • 25
  • 38