Power Platform/Learn/Array Actions

Array & Collection Actions

Power Automate's built-in Data Operations actions are the most underused feature by beginners. These are the most important ones to master. This guide explains each one clearly so you can work with lists of data without fighting loops.

Arrays in Power Automate

An array is simply a list of values or objects. In Power Automate, arrays appear constantly: SharePoint Get Items returns an array of list items. Forms Get Responses returns an array of submissions. HTTP APIs almost always return arrays in their response bodies.

In JSON (the data format Power Automate uses internally), arrays look like this:

Simple array of strings

["Alice", "Bob", "Charlie"]

Array of objects (typical SharePoint Get Items result)

[
  { "Id": 1, "Title": "Invoice Q1", "Amount": 1500, "Approved": false },
  { "Id": 2, "Title": "Invoice Q2", "Amount": 2300, "Approved": true  },
  { "Id": 3, "Title": "Invoice Q3", "Amount": 800,  "Approved": false }
]

Every action in the Data Operations category: Select, Filter Array, Compose, Create HTML Table, Create CSV Table and Join. Each, Filter Array, Compose, Create HTML Table, takes an array as its primary input and returns a transformed result.

Apply to Each

Apply to Each is a Control action that loops over every item in an array and runs a set of child actions for each one. It is the most common way to process multiple records.

Basic usage

Drag in an Apply to Each, then in the "Select an output from previous steps" field pick the array : for example the value output from Get Items. Then add actions inside the loop. Inside those actions, use items('Apply_to_each') or the dynamic content "Current item" to access the current record.

Reading fields inside the loop

Current item Title:   items('Apply_to_each')?['Title']
Current item Amount:  items('Apply_to_each')?['Amount']
Current item Author:  items('Apply_to_each')?['Author']?['Email']

Concurrency: Running iterations in parallel

By default, Apply to Each runs one iteration at a time (sequential). If your loop iterations are independent, enable concurrency in the action settings. You can run up to 50 iterations simultaneously, dramatically speeding up large loops.

Do not enable concurrency if iterations write to the same variable or depend on each other's output : race conditions will corrupt your data. It is safe only when each iteration is fully independent.

Select: Transform every item in an array

Select is a Data Operations action that maps over every item in an array and produces a new array where each item has been transformed. Think of it as "for every item, reshape it into this new form."

Select takes two inputs: the array to process (From), and a key/value mapping (Map) that defines the shape of each output item.

Example: extracting only the fields you need

SharePoint Get Items returns every field on every item : often 30 or more fields you don't need. Use Select to trim the array down to just the fields you care about.

Select Map configuration

From:   body('Get_items')?['value']

Map:
  Key: Title        Value: items()?['Title']
  Key: Email        Value: items()?['Author']?['Email']
  Key: Amount       Value: items()?['Amount']

Result: A clean array with only the fields you mapped

Input

[
  { Title: "Invoice Q1", Email: "a@co.com", Amount: 1500 },
  { Title: "Invoice Q2", Email: "b@co.com", Amount: 2300 }
]

Output

[
  { "Title": "Invoice Q1", "Email": "a@co.com", "Amount": 1500 },
  { "Title": "Invoice Q2", "Email": "b@co.com", "Amount": 2300 }
]

Example: flattening a nested field

Select can also compute new values using expressions in the value field.

Select Map: Adding a computed field

Key: FullName   Value: concat(items()?['FirstName'], ' ', items()?['LastName'])
Key: TaxAmount  Value: mul(items()?['Amount'], 0.2)
Key: DueDate    Value: addDays(items()?['InvoiceDate'], 30)

Select runs entirely server-side in a single step. It is vastly faster than an Apply to Each loop that reads fields one by one. For any scenario where you just need to reshape an array, always prefer Select over a loop.

Filter Array

Filter Array keeps only the items in an array that match a condition. It takes an array as input and outputs a smaller array containing only the matching items.

Basic mode: The visual condition builder

In the designer, Filter Array shows a condition builder similar to the Condition action. Set the left side to a field from the array item, pick an operator, and type or select the value to compare against.

Filter Array: Keep only approved invoices

From:         body('Get_items')?['value']

Condition:
  item()?['Approved']     is equal to     true

Advanced mode: Writing the condition as an expression

Click "Edit in advanced mode" to write the full condition as a single boolean expression. This gives you access to all logical and comparison functions.

Advanced mode expression

@and(
  equals(item()?['Status'], 'Active'),
  greater(item()?['Amount'], 1000)
)

Note the use of item() (no action name in brackets) inside Filter Array. This refers to the current item being evaluated : it is different from items('Apply_to_each') which is used inside loop actions.

What to do with the filtered result

The output of Filter Array is still an array. You can feed it into:

Apply to Each:   loop over the filtered items
Select:          reshape the filtered items
Create HTML Table: render them as a table
length():        count how many matched
empty():         check if any matched at all

Example: filtering invoices over £1000 with status Active

Input

[
  { "Title": "Invoice Q1", "Amount": 800,  "Status": "Active"   },
  { "Title": "Invoice Q2", "Amount": 2300, "Status": "Active"   },
  { "Title": "Invoice Q3", "Amount": 1500, "Status": "Archived" }
]

Output

[
  { "Title": "Invoice Q2", "Amount": 2300, "Status": "Active" }
]

Compose

Compose is a simple but incredibly useful action. Its only job is to evaluate an expression and store the result so you can reference it later. Think of it as a named constant or an intermediate variable without the overhead of a proper Variable action.

Using Compose to avoid re-typing expressions

If you need the same computed value in five different places, put it in a Compose action once and reference the output: outputs('Compose').

Compose: Compute once, use everywhere

Compose input:
  formatDateTime(utcNow(), 'yyyy-MM-dd')

Later in the flow:
  outputs('Compose_Date')   →  "2026-05-17"

Building a JSON object with Compose

Compose can hold any type : a string, number, boolean, array, or full JSON object. This is perfect for building a request body before sending it to an HTTP action.

Compose : building an HTTP request body

{
  "Title":       "@{triggerBody()?['Title']}",
  "SubmittedBy": "@{triggerBody()?['Author']?['Email']}",
  "SubmittedAt": "@{utcNow('yyyy-MM-ddTHH:mm:ssZ')}",
  "Score":       @{int(triggerBody()?['Score'])}
}

Use Compose to debug expressions. Point it at any expression, run the flow, and inspect the output in the run history to see exactly what value it resolved to.

Parse JSON

When Power Automate receives a string that contains JSON : from an HTTP response, a SharePoint multi-line field, or a webhook body : it treats it as plain text. Parse JSON turns that string into a typed object so you can access its fields using the dynamic content picker instead of writing expressions.

How it works

Parse JSON takes a Content input (your JSON string) and a Schema (the shape of the data). Once parsed, every field in the schema appears as a dynamic value in subsequent actions.

Parse JSON: Input

Content:  body('HTTP_Request')

Schema (auto-generated or written manually):
{
  "type": "object",
  "properties": {
    "id":     { "type": "integer" },
    "name":   { "type": "string"  },
    "active": { "type": "boolean" },
    "tags":   { "type": "array", "items": { "type": "string" } }
  }
}

Generating the schema automatically

Run the flow once to get a sample response, then click "Generate from sample" in the Parse JSON action, paste the sample, and the schema is built for you automatically.

If the actual data structure doesn't match the schema, the Parse JSON action will fail. If your API sometimes returns extra or missing fields, make the schema more permissive by removing required field declarations or using additionalProperties: true.

When to use it

HTTP connector response body:       always needs Parse JSON
SharePoint multi-line JSON fields:  if you store JSON there
Webhook payloads:                   if body arrives as a string
Compose outputs that are objects:   to get typed field access

Create HTML Table and Create CSV Table

Both actions take an array of objects and format them as a table. They are most useful when building email bodies or file outputs.

Create HTML Table

Takes an array, produces a fully formatted HTML table string. Use it in the body of a Send Email action with HTML enabled.

Create HTML Table

From:     outputs('Select')    (use Select first to pick the columns you want)
Columns:  Automatic (uses the keys from your objects as column headers)
          OR Custom (define your own column names and value expressions)

The output: A complete HTML table string

<table>
  <thead><tr><th>Title</th><th>Amount</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>Invoice Q1</td><td>1500</td><td>Active</td></tr>
    <tr><td>Invoice Q2</td><td>2300</td><td>Active</td></tr>
  </tbody>
</table>

Always run Select before Create HTML Table to control which columns appear and in what order. If you feed a raw Get Items result into Create HTML Table you will get 30+ SharePoint system columns in your table.

Create CSV Table

Identical to HTML Table but produces a CSV string instead. Combine with Create File to generate downloadable CSV reports in OneDrive or SharePoint.

Create CSV Table output

Title,Amount,Status
Invoice Q1,1500,Active
Invoice Q2,2300,Active

Join

Join turns an array into a single string by inserting a separator between each item. It is the Data Operations equivalent of the join() expression function.

Join: Array to comma-separated string

Input

["Finance", "IT", "HR"]

Separator: ", "

Output

"Finance, IT, HR"

A common use case is building a comma-separated list of approvers from a people field array, or joining tags for a SharePoint metadata field.

Practical example: Building a recipient list

From:       outputs('Select_Emails')   (array of email strings from Select)
Separator:  ";"

Result:     "alice@co.com;bob@co.com;charlie@co.com"
Use as:     the To field of Send an Email

Nested loops and performance

A nested loop is an Apply to Each inside another Apply to Each. They are sometimes necessary but always worth trying to avoid.

Why nested loops are slow

Each iteration of the outer loop triggers a full run of the inner loop. For 100 outer items each with 10 inner items, your flow executes 1,000 iterations plus all the action calls inside them. At scale this quickly hits Power Automate's 5,000 action limit per run or causes timeout errors.

Before building a nested loop, ask: can I use a single Filter Array or Select on the full dataset instead? These actions run in a single step and process the entire array server-side with no per-iteration overhead.

When nested loops are unavoidable

Acceptable:   Outer loop over 5-10 parent records, inner loop over a few children each
Risky:        Outer loop over 50+ records with connector calls in the inner loop
Dangerous:    Any loop containing API calls that could be rate-limited (throttling)

Mitigation:   Enable concurrency on the outer loop
              Pre-fetch all child data before looping and Filter Array inside the loop
              Use OData filter queries to reduce Get Items results before looping

Using Filter Array instead of a loop with a condition

A very common anti-pattern is an Apply to Each containing a Condition action that skips most items. Replace this with Filter Array before the loop, then only loop over the items you actually need to process.

Anti-pattern: Loop with internal condition

Apply to Each (all 500 items)
  > Condition: item Status equals Active
            Yes: Update SharePoint item
        └── No: Do nothing

Better approach: Filter first, then loop

Filter Array (all 500 items → only Active ones, e.g. 12)
Apply to Each (only the 12 Active items)
  > Update SharePoint item

Choosing the right action

Here is a quick reference for picking the right tool for each scenario:

GoalUse this
Loop over items and call an API or connector for eachApply to Each
Reshape every item (pick fields, compute values)Select
Keep only items matching a conditionFilter Array
Store a computed value for re-useCompose
Get typed access to a JSON string's fieldsParse JSON
Build an HTML table for an email bodyCreate HTML Table (after Select)
Export data to a CSV fileCreate CSV Table (after Select)
Build a semicolon or comma-separated string from an arrayJoin
Count items in an arraylength() expression
Check if an array is emptyempty() expression

Live Array Sandbox

See exactly what Select, Filter Array, Join, and Compose produce on a real array. Edit the input array or the action settings and the output updates instantly.

Select: Map

:
:

Available fields: , , ,

Output

5 items
[
  {
    "Title": "Invoice Q1",
    "Amount": 1500
  },
  {
    "Title": "Invoice Q2",
    "Amount": 2300
  },
  {
    "Title": "Invoice Q3",
    "Amount": 800
  },
  {
    "Title": "Invoice Q4",
    "Amount": 3100
  },
  {
    "Title": "Invoice Q5",
    "Amount": 450
  }
]

Power Automate equivalent

Data Operations > Select
From: body('Get_items')?['value']

Build OData filters for Get Items

Use the OData Filter Builder to write filter queries for SharePoint and Dataverse Get Items : reducing the data before it even reaches your flow. This is the most effective way to avoid large loop iterations.

Open builder