Shed some light on arrays or collections in Power Automate

Are you struggling with arrays in Power Automate? This post will help you master arrays.

Power Automate arrays and collection

Today I was asked about how arrays (aka collections) work in Microsoft Flow. In this post I will shed some light on arrays and many of the common scenarios that you may face with arrays in Power Automate.

I will start with creating arrays, then I will look at manipulating arrays, but first of all what are arrays?

In most common programming languages you will recognize the term array as a block of related data elements. You could see this as a table or a list list alike data structure.

Creating arrays

I’m going to start with the following table of data.

BrandColorAgeRegistration Date
NissanRed301/07/2015
CitroenGreen221/09/2016
AudiBlue401/01/2014

I want to create an array like the above table.

In flow I could now create an array using the createArray function.

createArray('Nissan','Citroen','Audi')

I could even take this further and create arrays in arrays.

createArray(createArray('Nissan','Red',3,'01/07/2015'),createArray('Ctiroen','Green',2,'21/09/2016'),createArray('Audi','Blue',4,'01/07/2014'))

Nested Arrays in Json

And I will get an array of arrays

[
  [
    "Nissan",
    "Red",
    3,
    "01/07/2015"
  ],
  [
    "Citroen",
    "Green",
    2,
    "21/09/2016"
  ],
  [
    "Audi",
    "Blue",
    4,
    "01/07/2014"
  ]
]

Now that we know how to create arrays, it is time to do something with all these arrays.

Where do I find arrays in flows?

Within Flow you can find arrays everywhere. The most common place and most visible place is within the Apply to each step. In this step flow will take an array and step through the elements in the array. So If we take a further look at the previous example then a Compose delivering an array can split by an Apply to each step:

As Power Automate is running through the Apply to each you will find the separate elements of my array.

So far this post is all about creating arrays and then stepping through arrays. Now that we have some basic understanding of arrays in Flow it is time to get some real arrays. For this I’m going to create a list in SharePoint with the same data as in the above table.

Using the Get Items action from the SharePoint connector I can now read the data from this list.

Looking at the Body of the Get Items action you will see some json as shown below:

{
  "value": [
    {
      "@odata.etag": "\"1\"",
      "ItemInternalId": "1",
      "ID": 1,
      "Title": "Nissan",
      "Colour": {
        "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
        "Id": 0,
        "Value": "Red"
      },
      "Colour#Id": 0,
      "Age": 3,
      "Registration_x0020_Date": "2015-01-07",
      "Modified": "2018-07-10T08:36:27Z",
      "Created": "2018-07-10T08:36:27Z",
      "Author": {
      "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
      "Claims": "i:0#.f|membership|pieter@pieterveenstradev.onmicrosoft.com",
      "DisplayName": "Pieter Veenstra",
      "Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
      "Picture": "https:/ /pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraMVP.onmicrosoft.com",
      "Department": null,
      "JobTitle": null
    },
    "Author#Claims": "i:0#.f|membership|pieter@pieterveenstradev.onmicrosoft.com",
    "Editor": {
       "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
       "Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
       "DisplayName": "Pieter Veenstra",
       "Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
       "Picture": "https:/ /pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraMVP.onmicrosoft.com",
       "Department": null,
       "JobTitle": null
    },
    "Editor#Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
    "{Identifier}": "Lists%252fCars%252f1_.000",
    "{Link}": "https://pieterveenstradev.sharepoint.com/_layouts/15/listform.aspx?PageType=4&ListId=51c5fad6-6c45-4bc4-8632-556674abe0be&ID=1&ContentTypeID=0x01004819843C002BE14D97CADA4CCA608281",
    "{Name}": "Nissan",
    "{FilenameWithExtension}": "Nissan",
    "{Path}": "Lists/Cars/",
    "{HasAttachments}": false
  },
  {
     "@odata.etag": "\"1\"",
     "ItemInternalId": "2",
     "ID": 2,
     "Title": "Citroen",
     "Colour": {
     "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
     "Id": 2,
     "Value": "Green"
   },
   "Colour#Id": 2,
   "Age": 2,
   "Registration_x0020_Date": "2016-09-23",
   "Modified": "2018-07-10T08:37:04Z",
   "Created": "2018-07-10T08:37:04Z",
   "Author": {
   "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
   "Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
   "DisplayName": "Pieter Veenstra",
   "Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
   "Picture": "https://pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraMVP.onmicrosoft.com",
   "Department": null,
   "JobTitle": null
 },
   "Author#Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
   "Editor": {
     "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
     "Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
     "DisplayName": "Pieter Veenstra",
     "Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
     "Picture": "https://pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraMVP.onmicrosoft.com",
     "Department": null,
     "JobTitle": null
   },
   "Editor#Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
   "{Identifier}": "Lists%252fCars%252f2_.000",
   "{Link}": "https:/ /pieterveenstradev.sharepoint.com/_layouts/15/listform.aspx?PageType=4&ListId=51c5fad6-6c45-4bc4-8632-556674abe0be&ID=2&ContentTypeID=0x01004819843C002BE14D97CADA4CCA608281",
   "{Name}": "Citroen",
   "{FilenameWithExtension}": "Citroen",
   "{Path}": "Lists/Cars/",
   "{HasAttachments}": false
},
{
"@odata.etag": "\"1\"",
"ItemInternalId": "3",
"ID": 3,
"Title": "Audi",
"Colour": {
"@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
"Id": 1,
"Value": "Blue"
},
"Colour#Id": 1,
"Age": 4,
"Registration_x0020_Date": "2014-01-01",
"Modified": "2018-07-10T08:37:42Z",
"Created": "2018-07-10T08:37:42Z",
"Author": {
"@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|pieter@pieterveenstradev.onmicrosoft.com",
"DisplayName": "Pieter Veenstra",
"Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
"Picture": "https://pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraDev.onmicrosoft.com",
"Department": null,
"JobTitle": null
},
      "Author#Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
      "Editor": {
        "@odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
        "Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
        "DisplayName": "Pieter Veenstra",
        "Email": "pieter@PieterVeenstraMVP.onmicrosoft.com",
        "Picture": "https://pieterveenstraMVP.sharepoint.com/_layouts/15/UserPhoto.aspx?Size=L&AccountName=pieter@PieterVeenstraDev.onmicrosoft.com",
        "Department": null,
        "JobTitle": null
      },
      "Editor#Claims": "i:0#.f|membership|pieter@pieterveenstraMVP.onmicrosoft.com",
      "{Identifier}": "Lists%252fCars%252f3_.000",
      "{Link}": "https://pieterveenstraMVP.sharepoint.com/_layouts/15/listform.aspx?PageType=4&ListId=51c5fad6-6c45-4bc4-8632-556674abe0be&ID=3&ContentTypeID=0x01004819843C002BE14D97CADA4CCA608281",
      "{Name}": "Audi",
      "{FilenameWithExtension}": "Audi",
      "{Path}": "Lists/Cars/",
      "{HasAttachments}": false
    }
  ]
}

This is where the data in Power Automate can become difficult to understand. The above JSON might need a second look before you really understand what data can be found. json can be difficult to understand, especially when a lot of json is thrown at you. Flow is making your life easy here! Use dynamic content and often you can forget about the json, you simply get your SharePoint list columns back. It even helps you with the complexities around lookups, dates and other column types.

Array functions

Time for a step into the deep end. There is a lot more we can do with arrays. I’m going to start with the arrays functions available within Microsoft Flow. You will notice that in the referenced article the term Collections is also used.

Collection functionTask
containsCheck whether a collection has a specific item.
emptyCheck whether a collection is empty.
firstReturn the first item from a collection.
intersectionReturn a collection that has only the common items across the specified collections.
joinReturn a string that has all the items from an array, separated by the specified character.
lastReturn the last item from a collection.
lengthReturn the number of items in a string or array.
skipRemove items from the front of a collection, and return all the other items.
takeReturn items from the front of a collection.
unionReturn a collection that has all the items from the specified collections.

Hey, I want to do more! Where is the sort function?

Well remember when we got the data from my SharePoint list? In the Get Items action you can sort your data. So you might want to sort your data when you collect your data.

Also in the Data operations connector there is no sort:

If you need to sort your array however, please have a look at Sort an array or collection in Power Automate

The examples of functions on Arrays

In this section I will look at operations you can do on arrays such as join, select and filter.

Contains

The contains functions checks your array for any values or elements in your collection or array.

Looking at the following 3 examples. The first 2 return false where the last one returns true.

contains(body('Get_items')?['Value'],'Nissan')

contains(outputs('Compose'),'Nissan')

contains(first(outputs('Compose')),'Nissan')

Within the last example I selected a single record from my array before checking with the contains function for my car brand. Where in the first two examples I’m supplying an array of array to the contains function.

Empty

Empty checks the length of the Collection if no items are found in the array then true is returned. Note that this is not the same as comparing an array to a null value.

First item in collections

The First function returns the first item from the collection. See the 3rd example from the contains section above for a real example.

You could also user the construction array[0] to get to the first item, however when the array isn’t defined you will find that your flows will error while first returns a null value.

Intersection

Imagine that you have two arrays and you want to get the items that exist in both collection. and Now you want to find the items that exist in both collections.

So you could for example have two lists with cars. One list is called my cars and one is called insured cars if you now want to find out which of your cars are insured you could use the intersection function.

intersection(body('Get_items')?['Value'],body('Get_items_2')?['Value'])

Join collections

When you have an array of strings and you would like to create a character separated list of strings then you could do this with the join function. Have you ever has a list of users in SharePoint and you wanted to email them all?

join(outputs('Compose'),';')

Last item in collections

The last function is similar the the first function, other than that you get the last element in the array rather than the first one.

Length of collections

The length function gives you the number of elements in a collection. Nothing exciting here.

Skip

With the skip function you can select all elements after a certain position.

skip(outputs('Compose'),2)

Take

The take function is similar to the skip function but now you can collect all the elements up to a certain point in your array.

take(outputs('Compose'),2)

Union

In my earlier examples I had two arrays in my json. One that I manually created and and one that I get from the Get Items action. To merge these arrays into one array you could run the following:

union(body('Get_items')?['Value'],outputs('Compose'))

Did you notice that Flow really doesn’t care that these two arrays are not exactly the same. As Flow will just consider these two arrays to be JSON. It is just gluing the two collections of data together. this is where a sort might be important, although most of the times you will find that you will just want to process each element in your collection and the order really doesn’t matter.

Select

The select action I also want to include here. Although Select is not a function within Power Automate it can help you transform an array of elements.

Taking an input array and transforming the arrays using a select often helps improve the processing of arrays.

Filter arrays

If you want to filter your array or collection data in Power Automate then please have a look at my filter data in arrays using the select action posts. I that post I’m giving more details on filtering data using the select.

Often it can be wise to filter an array first before pushing it through an apply to each. For performance reasons this is definitely worth a look. It is quite easy to gain multiple minute in the run of a flow.

Share
Pieter Veenstra

Business Applications and Office Apps & Services Microsoft MVP working as a Microsoft Productivity Principal Consultant at HybrIT Services. You can contact me using contact@veenstra.me.uk.

View Comments

  • Hi,

    I came around here to find a solution for my problem, it is really useful, I'm not going to lie.

    However, I have a small problem with my current scenario (it is a bit weird for some people).

    I tried to create a very simple array (for storing weighted score that has 4 score in each question that answered in Microsoft Forms). I already created logic behind. However, when I want to add new item into array, I don't really know how to do so.

    For example:

    I have empty array in "Result", and I want to include weights, which are 0, 0.33, 0.66, and 1. There is 70 questions in the surveys.

    Expected results should be something like this:

    [0.66, 0.66, 0.33, …, 1]

    Is it possible to do so? If not, what is the best possible way to do this?

    • Hi Kittanan,

      As you are having a set length array i would go for the easy option

      In the image below refer to each element in the array using the code below

      outputs('Compose')[0]

      this will then result in the following flow run:

      So I inserted a value in the middle of an array.

      Does this help?

  • Hi,

    First of all, sorry for extremely late reply. Second, thanks for pointing out, however the solution doesn't work with my current algorithm.

    Currently, I use Apply to Each in each loop for storing answers (and put them on Excel Online). The structure of project is involved with Microsoft Forms and relied on real-time data.

    My solution is to create answer that filled in from the forms as JSON files (named Answers). Then, I match the answers with the weight (with another JSON file, named Questions). Then, I store the answers to array (named Results).

    Here is the psudo-code of the flow that I am running for my project:

    for (questionList = 1; questionList <= 70; questionList++)
    for (scoreList = 1; scoreList <= Question[questionList].answers.length(); scoreList++)
    if (Answers[questionList].name == Question[questionList].answers[scoreList].name)
    then Results[questionList] = Question[questionList].answers[scoreList].weight;

    The problem is, my current solution is not efficient enough and I want to run parallelly.

    So, if you have any solution for this, I would really appreciate for that.

  • Hi Pieter,

    I'm trying to use an array variable in the Odata filter for a Get Records action without success.

    Specifically, i have an array variable called vUserIds which contains 3 elements.
    In the filter field for the Get Records action I need to filter the list of return records by the Ids in the above variable.

    I had hoped that the following would work:

    "Id in vUserIds"

    However, the Dynamic Content dialog doesn't give me the option of choosing the array variable.

    So, can you advise on how I can achieve the above?

    Thanks in advance
    Jon

    • Hi Jon,

      Sorry for the delay. Your comment ended up in Spam.

      There are two ways that you can do this.

      1. Use the filter action and then count the number of items found.
      2. Join the 3 strings together ( using the join function) this could give you text1;text2;text3 Now you can do a textual search. within that joined up text.

  • Hi Pieter,
    Thanks for this useful post!
    In looping through an array, what is the best way to determine if the current item is the last one in the array?
    I thought of comparing last(body('Filter_array name')) but couldn't figure out the way to retrieve current item's value or index...

    • The expression in the second compose is as follows

      equals(last(outputs('Get_items')?['body/value']), outputs('Compose'))

  • Pieter,

    I haven't seen solutions for array element assignment without using take, array, skip, and union. Union causes drops of array elements where duplicates occur. As such, I've come up with a solution using Select and Set Variable actions.

    Select:
    From: @{range(0,length(variables('~~my array~~')))}
    Map: @{if(equals(~~my index~~,item()),~~new array element value~~,variables('~~my array~~')[item()])}

    Set Variable:
    Name: ~~my array~~
    Value: @{body('Select')}

    Tweaking the second parameter of the range function to max() of length of the array and the index could be used for array element assignment with extension. The map would need to fill extended elements before index with an empty value.

    Cheers,
    Marty

    • Hi Marty,

      I like the idea. I would only use a compose instead of a variable. Variables in branches are slow and I wonder if a compose would be faster.

      Anyways like I said I like the uses of Select, they are so much faster than Apply to each.

  • I agree. However, in a flow I am building there are concurrent steps that need to reference the resulting array globally. A compose action would only be local to that line of steps, but not outside in a parallel line.

    On thing to consider and perhaps test for are race conditions on assignment with parallelism occurring. I could envision a flow action may be atomic, but not a series of steps -- e.g. Select + Set Variable. A concurrent thread could in theory execute a separate Select + Set on the same array in between the execution of another (slower) thread executing Select + Set and changes are lost when the first (slower) thread finishes the Set action after the second (faster) thread.

      • Follow up:

        (1) I didn't describe the use case well enough and hence I think there is confusion with concurrency control and degree of parallelism performance versus my problem. What I have is several asynchronous parallel branches in the flow. All the parallel branches will set their respective element in the array at some non-deterministic time.

        (2) I created a simple example flow to check for race conditions. All parallel branches perform the same actions. The result was that there is indeed a race condition between Select and Set Variable actions of different branches -- even with Concurrency Control on and Degree of Parallelism 1. In theory it makes sense as flow parallel branches are non-deterministic finite automata. Therefore a semaphore/monitor-esque solution would be needed to control entry and exit to the Select + Set actions preventing competing parallel branches from overwriting each other.

        (3) Time to log a new request for a new atomic action to Set Array Element in the Variable actions... https://powerusers.microsoft.com/t5/Power-Automate-Ideas/idb-p/MPAIdeas

  • hi Pieter, I do find your posts really handy . With my issue, I easily filtered an array in my Power Automate (item()?['DocumentType'] and have now have [{"DocumentType":"Policy","Importance":1}]

    Next, I would like the Importance value but so far none of these espressions work
    outputs('My_compose' )?['Importance']
    outputs('My_compose')[1]?['Importance']

    Btw .. I didn't see the array functions like "take" appear under the Expression selector ..
    Aforeach loop seems like overkill, even though this should work.
    Any thoughts please

    • Hi Daniel,

      you got close.

      This will work:

      Outputs('...')[0]?['Importance']

      Also you could use:

      First(outputs('...'))?['Importance']

  • Pieter , thanks very much for replying. I woke up this morning thinking I had to use [0] to reference the first item in the array ( similar to PowerShell ) but I am glad you have confirmed this . Out of interest, are functions like "take" only available to logic apps ? regards Daniel

Recent Posts

Get started with adaptive cards in Power Automate

Getting started with adaptive cards can be difficult. In this post i have written some…

6 days ago

Create PDF documents from data in Power Automate

In this post I will look at how to create PDF documents from data. Use…

7 days ago

Unnest nested arrays in Power Automate

We all know this problem, you have a nested array in Power Automate but how…

2 weeks ago

Advanced settings not loading in Power Platform

Yesterday one of my clients showed me an issues where the Advanced settings didn't load.…

3 weeks ago

1 Better way to use Create Item in SharePoint with Power Automate

Have you been using the REST API instead of the create item action in Power…

3 weeks ago

Filter by Content Type using Get Items in Power Automate.

In a comment on my Filter Query on Get Items in the SharePoint connector post…

3 weeks ago
%%footer%%