# map

> For the complete documentation index, see [llms.txt](https://kotlin.github.io/dataframe/llms.txt).

Computes a new value for every value, row, or key–group pair of the receiver, and collects the results into a [List](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/), a [DataFrame](dataframe.html), a [DataColumn](datacolumn.html), or a [FrameColumn](datacolumn.html#framecolumn).

API reference: [map](https://kotlin.github.io/dataframe/api-docs/core/org.jetbrains.kotlinx.dataframe.api/map.html)

Related operations: [Add / map / remove columns](addremove.html)

All `map` operations share the name but differ in what they go over and what they give back:

| Operation | Goes over | Returns |
| --- | --- | --- |
| [map](#map) | rows of a [DataFrame](dataframe.html) | `List` |
| [mapToColumn](#maptocolumn) | rows of a [DataFrame](dataframe.html) | a single [DataColumn](datacolumn.html) |
| [mapToFrame](#maptoframe) | rows of a [DataFrame](dataframe.html) | a new [DataFrame](dataframe.html) |
| [map](#map-on-datacolumn) / [mapIndexed](#map-on-datacolumn) | values of a [DataColumn](datacolumn.html) | a [DataColumn](datacolumn.html) of the same size |
| [map](#map-on-groupby) | key–group pairs of a [GroupBy](groupby.html) | `List` |
| [mapToRows](#map-on-groupby) | key–group pairs of a [GroupBy](groupby.html) | a [DataFrame](dataframe.html) |
| [mapToFrames](#map-on-groupby) | key–group pairs of a [GroupBy](groupby.html) | a [FrameColumn](datacolumn.html#framecolumn) |

Each result keeps the order of the values, rows, or key–group pairs it was computed from.

Every example on this page uses the same [DataFrame](dataframe.html):

```KOTLIN
df
```

[Embedded content](./resources/mapDf.html)

## map

Maps the rows of a [DataFrame](dataframe.html) into a `List` with one element per row.

```TEXT
map { rowExpression }: List<T>

rowExpression: DataRow.(DataRow) -> Value
```

```KOTLIN
df.map { 2021 - it.age }
```

See [row expressions](datarow.html#row-expressions)

A [ColumnGroup](datacolumn.html#columngroup) is also a [DataFrame](dataframe.html), so `map` on a column group is this operation: it goes over the rows of the group and returns a `List`. To get a [DataColumn](datacolumn.html) of the same size instead — a column of the rows of the group — call `asDataColumn()` on the group first, and then [map](#map-on-datacolumn).

## mapToColumn

Maps the rows of a [DataFrame](dataframe.html) into a single new [DataColumn](datacolumn.html) with one value per row.

```TEXT
mapToColumn(columnName) { rowExpression }: DataColumn

rowExpression: DataRow.(DataRow) -> Value
```

Properties:

```KOTLIN
df.mapToColumn("year of birth") { 2021 - age }
```

Strings:

```KOTLIN
df.mapToColumn("year of birth") { 2021 - "age"<Int>() }
```

[Embedded content](./resources/mapToColumn.html)

See [row expressions](datarow.html#row-expressions)

The new column is standalone: the original [DataFrame](dataframe.html) is not changed and does not contain it. Use [add](add.html) to get a [DataFrame](dataframe.html) with the new column in it.

Inside the row expression, `prev()?.newValue()` gives the value already computed for the preceding row — `null` for the first row, where there is no preceding one. This is how running totals and other recurrences are expressed; see [add](add.html).

## mapToFrame

Maps the rows of a [DataFrame](dataframe.html) into a new [DataFrame](dataframe.html) made of the described columns.

```TEXT
mapToFrame { 
    columnMapping
    columnMapping
    ...
} : DataFrame

columnMapping = column into columnName | columnName from column | columnName from { rowExpression } | +column  
```

Properties:

```KOTLIN
df.mapToFrame {
    "year of birth" from { 2021 - age }
    expr { age > 18 } into "is adult"
    name.lastName.map { it.length } into "last name length"
    "full name" from { name.firstName + " " + name.lastName }
    +city
}
```

Strings:

```KOTLIN
df.mapToFrame {
    "year of birth" from { 2021 - "age"<Int>() }
    expr { "age"<Int>() > 18 } into "is adult"
    "name"["lastName"]<String>().map { it.length } into "last name length"
    "full name" from { "name"["firstName"]<String>() + " " + "name"["lastName"]<String>() }
    +"city"
}
```

[Embedded content](./resources/mapMany.html)

The result holds only the described columns, in the order in which they are described. This is what makes `mapToFrame` different from [add](add.html), where the columns of the original [DataFrame](dataframe.html) are part of the result as well. In the example above, `city` is in the result only because of the `+city` line, and `name` and `age` are not in it at all.

## map on DataColumn

Maps the values of a [DataColumn](datacolumn.html) into a new [DataColumn](datacolumn.html) of the same size. `mapIndexed` gives the position of the value as well, starting at `0`.

```TEXT
map { value -> newValue }: DataColumn
map(type) { value -> newValue }: DataColumn

mapIndexed { index, value -> newValue }: DataColumn
mapIndexed(type) { index, value -> newValue }: DataColumn
```

```KOTLIN
// A column of last name lengths; it keeps the name of the original column,
// so it is renamed here
df.name.lastName.map { it.length }.rename("lastNameLength")
```

[Embedded content](./resources/mapOnColumn.html)

The new column has the same name as the original one, so it is usually renamed on the spot or given a name by the operation it is passed to.

`mapIndexed` also gives the position of the value:

```KOTLIN
// "1. Alice", "2. Bob", ...
df.name.firstName.mapIndexed { i, firstName -> "${i + 1}. $firstName" }
```

[Embedded content](./resources/mapIndexedOnColumn.html)

Which kind of column you get follows the type of the new column — the type argument, or the `type` given explicitly — and not the computed values: a [DataFrame](dataframe.html) type gives a [FrameColumn](datacolumn.html#framecolumn), a [DataRow](datarow.html) type gives a [ColumnGroup](datacolumn.html#columngroup), and any other type gives a [ValueColumn](datacolumn.html#valuecolumn). A nullable [DataFrame](dataframe.html) type belongs to the last group, because a [FrameColumn](datacolumn.html#framecolumn) cannot hold `null`.

`infer` only concerns a [ValueColumn](datacolumn.html#valuecolumn): it decides whether the type of that column is the given one as it is, or the type of the computed values. For a [ColumnGroup](datacolumn.html#columngroup) and a [FrameColumn](datacolumn.html#framecolumn) it changes nothing.

The overloads with an explicit `type` are for the cases where the type of the new column is only known at runtime. The computed values are put into the column as they are, without any conversion, so the type has to fit them.

A [ValueColumn](datacolumn.html#valuecolumn) can never have a non-nullable [DataFrame](dataframe.html) type, so a call that would give it one fails with an `IllegalArgumentException`. That happens when the computed values are dataframes and `Infer.Type` derives a [DataFrame](dataframe.html) type for them, and also under a nullable [DataFrame](dataframe.html) type when none of the computed values is `null`: the default `Infer.Nulls` then drops the nullability and leaves exactly that forbidden type. With at least one `null` among them the same call succeeds and gives a [ValueColumn](datacolumn.html#valuecolumn) of the nullable type.

## map on GroupBy

Maps the key–group pairs of a [GroupBy](groupby.html): every row of a `GroupBy` is one key–group pair — the key values, and the group of rows that belongs to them (see [groupBy](groupby.html)).

```TEXT
map { GroupWithKey -> value }: List<Value>
mapToRows { GroupWithKey -> DataRow? }: DataFrame
mapToFrames { GroupWithKey -> DataFrame }: FrameColumn
```

The lambda receives the pair as a `GroupWithKey`, both as the receiver and as the argument, so the key values are available as `key` (a [DataRow](datarow.html)) and the rows of the group as `group` (a [DataFrame](dataframe.html)).

```KOTLIN
// The number of people per city, as a list, in the order of the groups: [1, 1, 2, 1, 1, 1]
df.groupBy { city }.map { group.rowsCount() }
```

```KOTLIN
// The oldest person of every city, one row per city
df.groupBy { city }.mapToRows { group.sortByDesc { age }.firstOrNull() }
```

[Embedded content](./resources/mapToRowsOnGroupBy.html)

```KOTLIN
// The two oldest people for each first name, as a frame column:
// every frame keeps all the columns of the original, including the first name it was grouped by
df.groupBy { name.firstName }.mapToFrames { group.sortByDesc { age }.take(2) }
```

[Embedded content](./resources/mapToFramesOnGroupBy.html)

`mapToFrames` names the new column after [GroupBy.groups](groupby.html) (`"group"` by default); call `concat()` on it to get all of those dataframes back as one [DataFrame](dataframe.html). [concatWithKeys](groupby.html#concatwithkeys-on-groupby) is built exactly this way.

> **Note:**
> `map` and `mapToRows` leave out the pairs for which the lambda returns `null`, so their results can be shorter than the number of key–group pairs.

