@map

Creates a new tree from another by applying mapping functions to the original tree’s values and/or keys.

Mapping values

In the simplest form of @map, you provide a tree and a function that will be applied to the tree’s values. The resulting tree will have the same keys as the original:

$ cat greetings.yaml
Alice: Hello, Alice.
Bob: Hello, Bob.
Carol: Hello, Carol.
$ cat uppercase.js
export default (x) => x.toString().toUpperCase();
$ ori @map(greetings.yaml, uppercase.js)
Alice: HELLO, ALICE.
Bob: HELLO, BOB.
Carol: HELLO, CAROL.
g Alice Hello, Alice. ->Alice Alice Bob Hello, Bob. ->Bob Bob Carol Hello, Carol. ->Carol Carol
g Alice HELLO, ALICE. ->Alice Alice Bob HELLO, BOB. ->Bob Bob Carol HELLO, CAROL. ->Carol Carol
Original tree
Mapped values

Unlike a JavaScript Array map, the @map function does not do any mapping work upon invocation — it only does the work when someone requests the mapped tree’s keys or values.

The mapping function is typically a JavaScript function, an Origami lambda (unnamed function), or a template such as an Origami template. You can also use a second tree as a map.

@map options

In the basic form of @map shown above, the second parameter is some kind of mapping function that will be applied to the tree’s values. You can also use an expanded form of @map in which the second parameter is a collection of options:

$ ori @map(greetings.yaml, { valueMap: uppercase.js })

Available options include:

  • deep: If false (the default), this only maps the top-level values in the tree. If true, this maps values at all levels of the tree.
  • extensions: See below.
  • keyMap: A function that will be applied to the original tree’s keys. See below.
  • valueMap: A function that will be applied to the original tree’s values.

The value and key being mapped in scope

The @map function will put the value and key being mapped into the current scope. You can refer to the value being mapped as an _ underscore, and the key being mapped as @key.

The example above uses uppercase.js as the function that will be applied to each value. The example can also be written using a lambda function:

$ ori @map(greetings.yaml, =uppercase.js(_))
Alice: HELLO, ALICE.
Bob: HELLO, BOB.
Carol: HELLO, CAROL.

You could pass the key being mapped instead of the value to the uppercase.js function:

$ ori @map(greetings.yaml, =uppercase.js(@key))
Alice: ALICE
Bob: BOB
Carol: CAROL

The value and key being mapped are also available as function arguments; see below.

Mapping keys

You can call @map with a keyMap option to apply a function to a tree’s keys. For example, if a tree has keys which are names like “Alice”, you can map those keys to ones that end in “.html”, like “Alice.html”.

$ cat greetings.yaml
Alice: Hello, Alice.
Bob: Hello, Bob.
Carol: Hello, Carol.
$ ori "@map(greetings.yaml, { keyMap: =`{{ @key }}.html` })"
Alice.html: Hello, Alice.
Bob.html: Hello, Bob.
Carol.html: Hello, Carol.
g Alice Hello, Alice. ->Alice Alice Bob Hello, Bob. ->Bob Bob Carol Hello, Carol. ->Carol Carol
g Alice.html Hello, Alice. ->Alice.html Alice.html Bob.html Hello, Bob. ->Bob.html Bob.html Carol.html Hello, Carol. ->Carol.html Carol.html
Original tree
Mapped keys

Transforming extensions

The above example of changing a key’s extension is very common. Mapping values often changes the type of the data, and it is useful to be able to reflect that change in type in file extensions.

To facilitate changing extensions in a @map, you can supply an extensions option that indicates whether and how the extensions of the original tree should be changed:

  • extensions: "md" restricts the map to only apply to keys ending in .md
  • extensions: "->html" adds the .html extension to the keys in the result
  • extensions: "md->html" only applies the map to keys ending in .md, and adds the .html extension to keys in the result

In place of the -> text, you can alternatively write a Unicode Rightwards Arrow, as in "md→html". You can optionally include the . in extensions: { extension: ".md" }.

So you can also write the above example as:

$ cat greetings.yaml
Alice: Hello, Alice.
Bob: Hello, Bob.
Carol: Hello, Carol.
$ ori "@map(greetings.yaml, { extensions: '→html' })"
Alice.html: Hello, Alice.
Bob.html: Hello, Bob.
Carol.html: Hello, Carol.

Value and key being mapped as function arguments

As mentioned above, the @map function puts the value and key being mapped in scope. @map also passes the value being mapped, the key being mapped, and the tree being mapped as arguments to both the valueMap and keyMap functions.

This lets you choose names for the value and key that are more meaningful in your context.

$ ori "@map(greetings.yaml, (name, greeting) => uppercase.js(name))"
Alice: HELLO, ALICE.
Bob: HELLO, BOB.
Carol: HELLO, CAROL.
$ ori "@map(greetings.yaml, (name, greeting) => uppercase.js(greeting))"
Alice: ALICE
Bob: BOB
Carol: CAROL