# Sequences

A **sequence** is a composite data type representing an ordered list of values. If you are familiar with other programming languages, you may recognize sequences as "lists", "arrays", or (in some cases) "tables".

### Creating sequences

Sequences may be created using *sequence expressions,* comprising of an opening square bracket, a comma-separated list of elements, and a closing square bracket.

```typescript
// Example: Create a sequence of numbers
let xs = [1, 2, 3];

// Exemple: Create a sequence of records
let animals = [
    { name: "Eurasian lynx", latinName: "Lynx lynx" },
    { name: "Wolverine", latinName: "Gulo gulo" }
];
```

A sequence of records is known, by convention, as a *table*.

Sequences are typically used in conjunction with [queries](https://docs.novacura.com/flow-connect/reference/reference/flowscript/walkthrough/queries), [sequence-related functions](https://docs.novacura.com/flow-connect/reference/reference/flowscript/built-in-functions#sequence-related-functions) and the [concat operator](https://docs.novacura.com/flow-connect/reference/reference/flowscript/arithmetic#concat-and).

### Updating sequences of records

You can also update (or rather, create an updated copy) of a sequence of records using the **with** keyword.&#x20;

```typescript
// Example: create a sequence of cities and then create an updated copy.

let cities = [
    { name: "Stockholm", country: "Sweden" },
    { name: "Esch-sur-Alzette", country: "Luxembourg" },
];

return cities with { countryCode: upper(left(country, 3)) };
```

A with-expression may employ a *guard expression* to selectively update records:

```typescript
let measurements = [
    { temperature: 7, unit: "Celsius" },
    { temperature: 53, unit: "Fahrenheit" },
]

// Example: convert all Fahrenheit measurements to Celsius
return measurements with {
    temperature: (temperature - 32) * 5/9,
    unit: "Celsius"
} when unit = "Fahrenheit";
```

Several update clauses may be specified in a comma-separated list. The first clause whose guard expression is true for a record will be used to update that record. The `otherwise` guard expression always evaluates to true and must come last.

```typescript
let distances = [
    { value: 10, unit: "feet" },
    { value: 67, unit: "centimeters" }
]

// Example: convert all measurements to meters
return distances
    with { value: 0.3048 * value, unit: "meters" } when unit == "feet",
         { value: value / 100, unit: "meters" } otherwise;
```

### Accessing columns

For sequences of records, it is possible to access each "column" by dot notation. This will return a sequence containing the value of the member on each element.

```typescript
let users = [
    {id: 0, name: "Harinder"},
    {id: 1, name: "Natasha"}
];

return users.name; // returns ["Harinder", "Natasha"]
```

### Sequence types

The name of a sequence type – as shown when you let the mouse cursor hover over a variable name in the Flow Designer – is created by appending an asterisk (\*) to the type of the sequence's elements. In the example above, the variable **xs** contains numbers and therefore has the type `number*` (pronounced "sequence of numbers"). The variable **animals** has the type `{name: text, latinName: text}*` which is slightly more difficult to pronounce.

<details>

<summary>Advanced: The type of an empty sequence</summary>

The type of a sequence is inferred from its elements. What, then, is the type of an empty sequence? Since there are no elements from which to infer an element type, FlowScript will choose the type **nothing\*** for such a sequence. In conjunction with [type evolution](#advanced-evolving-variable-types) and [subtyping rules](https://docs.novacura.com/flow-connect/reference/reference/flowscript/walkthrough/appendix-subtyping-rules), this ensures that the type of the empty sequence behaves well when combining with other, non-empty values.

```typescript
let xs = []; // Here, xs has type nothing*
set xs = xs & [1, 2]; // Now, xs has type number*
```

</details>

<details>

<summary>Advanced: Mixed-type sequences</summary>

What if we create a sequence where the elements have *different* types? In that case, the element type of the sequence will be the [lowest common supertype](https://docs.novacura.com/flow-connect/reference/reference/flowscript/walkthrough/appendix-subtyping-rules) of all the elements.

```typescript
// Example: create two sequences where the elements have different types.
let mix1 = [
    { name: "Red fox", latinName: "Vulpes vulpes" },
    { name: "Yeti" } // Note: second item has no 'latinName' member
]

let mix2 = [1, 2, {name: "Panther"}];
```

In the example above, **mix1** has the type `{name: text}*`, which is the most useful common type for all the elements, and **mix2** has the type `unknown*`, signalling that the elements have nothing at all in common.

Although the type of mix1 does not indicate the presence of a member labelled latinName, the member has not been erased. You can use [type narrowing](https://docs.novacura.com/flow-connect/reference/reference/flowscript/other-types#advanced-type-narrowing) to get it back:

```typescript
// Example: use type narrowing to list the latin names in "mix1"
// as defined in the previous example.

let allLatinNames = [];
for animal in mix1 {
    if animal is {latinName: text} {
        set allLatinNames = allLatinNames & animal.latinName;
    }
}

// allLatinNames == ["Vulpes vulpes"]
```

</details>
