Working with Records
Tips
Records are roughly equivalent to the individual rows of a table. You can think of a record as essentially being a "one-row table". Thus, most commands which operate on a table row also operates on a record. For instance, update can be used with records:
let my_record = {
name: "Sam"
age: 30
}
$my_record | update age { $in + 1 }
# => ╭──────┬─────╮
# => │ name │ Sam │
# => │ age │ 31 │
# => ╰──────┴─────╯Note that the my_record variable is immutable. The updated record resulting from the pipeline is printed as seen in the code block. The my_record variable still holds the original value - $my_record.age is still 30.
Creating records
A record is a collection of zero or more key-value pair mappings. It is similar to a JSON object, and can be created using the same syntax:
# Nushell
{ "apples": 543, "bananas": 411, "oranges": 0 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => ╰─────────┴─────╯
# JSON
'{ "apples": 543, "bananas": 411, "oranges": 0 }' | from json
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => ╰─────────┴─────╯In Nushell, the key-value pairs of a record can also be separated using spaces or line-breaks.
Tips
As records can have many fields, they are, by default, displayed vertically rather than left-to-right. To display a record left-to-right, convert it to a nuon. For example:
{
name: "Sam"
rank: 10
} | to nuon
# => {name: Sam, rank: 10}Updating Records
As with lists, you can insert values in records. For example, let's add some pears:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| insert pears { 21 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => │ pears │ 21 │
# => ╰─────────┴─────╯You can also update values:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| update oranges { 100 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 100 │
# => ╰─────────┴─────╯To make a copy of a record with new fields, you can either:
Use the
mergecommand:let first_record = { name: "Sam", rank: 10 } $first_record | merge { title: "Mayor" } # => ╭───────┬───────╮ # => │ name │ Sam │ # => │ rank │ 10 │ # => │ title │ Mayor │ # => ╰───────┴───────╯Use the spread operator (
...) to expand the first record inside a new record:let first_record = { name: "Sam", rank: 10 } { ...$first_record title: "Mayor" } # => ╭───────┬───────╮ # => │ name │ Sam │ # => │ rank │ 10 │ # => │ title │ Mayor │ # => ╰───────┴───────╯
Iterating over a Record
Use the items command to iterate over each pair of key and value:
{ "apples": 543, "bananas": 411, "oranges": 0 } | items {|fruit, count| $"We have ($fruit) ($count)" }
# => ╭───┬─────────────────────╮
# => │ 0 │ We have apples 543 │
# => │ 1 │ We have bananas 411 │
# => │ 2 │ We have oranges 0 │
# => ╰───┴─────────────────────╯Alternatively, you can transpose the record into a table with columns, and then iterate over rows:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| transpose fruit count
| each {|f| $"We have ($f.count) ($f.fruit)" }
# => ╭───┬─────────────────────╮
# => │ 0 │ We have 543 apples │
# => │ 1 │ We have 411 bananas │
# => │ 2 │ We have 0 oranges │
# => ╰───┴─────────────────────╯Transforming Records
The record family of commands takes a record and returns a record, so you don't need to transpose it into a table and back, or rebuild it from items with into record. Each of them also accepts a table and works on every row.
record where keeps the fields that match a row condition on key and value:
{ "apples": 543, "bananas": 411, "oranges": 0 } | record where value > 0
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => ╰─────────┴─────╯
{ "apples": 543, "bananas": 411, "oranges": 0 } | record where key starts-with b
# => ╭─────────┬─────╮
# => │ bananas │ 411 │
# => ╰─────────┴─────╯It also takes a closure, which receives the key and the value as arguments. With --keys or --values, placed before the condition, the closure gets only one of them:
{ "apples": 543, "bananas": 411, "oranges": 0 } | record where --values {|count| $count > 500 }
# => ╭────────┬─────╮
# => │ apples │ 543 │
# => ╰────────┴─────╯record each builds a new record from the records its closure returns, so it can change keys and values in one pass:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| record each {|fruit, count| {($fruit | str uppercase): ($count * 2)} }
# => ╭─────────┬──────╮
# => │ APPLES │ 1086 │
# => │ BANANAS │ 822 │
# => │ ORANGES │ 0 │
# => ╰─────────┴──────╯record walk runs a closure on every value nested anywhere inside the record and puts the result back in the same place. The closure receives the value and its cell path. For example, to hide every secret in a configuration record:
{db: {host: prod, password: hunter2}, api: {token: abc}}
| record walk {|value, path|
if ($path | to text) =~ "(?i)secret|token|password" { "***" } else { $value }
}
| to nuon
# => {db: {host: prod, password: ***}, api: {token: ***}}By default, only the leaves are visited. --containers also passes the nested records and lists to the closure, and --no-lists treats a list as a single value instead of walking into it.
record apply takes a record of closures and runs each one on the field with the same name. Fields without a closure are left alone:
{age: "42", email: "A@B.COM", tags: [x]} | record apply {age: {into int}, email: {str lowercase}}
# => ╭───────┬───────────────╮
# => │ age │ 42 │
# => │ email │ a@b.com │
# => │ tags │ [list 1 item] │
# => ╰───────┴───────────────╯Because a table is a list of records, the same record of closures converts a column in every row:
[{name: a, size: "1"} {name: b, size: "2"}] | record apply {size: {into int}}
# => ╭───┬──────┬──────╮
# => │ # │ name │ size │
# => ├───┼──────┼──────┤
# => │ 0 │ a │ 1 │
# => │ 1 │ b │ 2 │
# => ╰───┴──────┴──────╯A closure for a field that the input doesn't have is ignored, unless you pass --strict, which makes it an error.
Accessing Record Values
See Navigating and Accessing Structured Data for an in-depth explanation of how to access record values (and other structured data).
Other Record Commands
See Working with Tables - Remember, commands that operate on table rows will usually operate the same way on records.