Nushell
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
  • 한국어
GitHub
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
  • 한국어
GitHub
  • Introduction
  • Installation
    • Default Shell
  • Getting Started
    • Quick Tour
    • Moving Around the System
    • Thinking in Nu
    • Nushell Cheat Sheet
  • Nu Fundamentals
    • Types of Data
    • Loading Data
    • Pipelines
    • Working with Strings
    • Working with Lists
    • Working with Records
    • Working with Tables
    • Navigating and Accessing Structured Data
    • Special Variables
  • Programming in Nu
    • Custom Commands
    • Aliases
    • Operators
    • Variables
    • Control Flow
    • Scripts
    • Modules
      • Using Modules
      • Creating Modules
    • Overlays
    • Sorting
    • Testing your Nushell Code
    • Debugging Scripts
    • Best Practices
  • Nu as a Shell
    • Configuration
    • Environment
    • Stdout, Stderr, and Exit Codes
    • Running System (External) Commands
    • How to Configure 3rd Party Prompts
    • Directory Stack
    • Reedline, Nu's Line Editor
    • Custom Completions
    • Externs
    • Coloring and Theming in Nu
    • Hooks
    • Background Jobs
  • Coming to Nu
    • Coming from Bash
    • Coming from CMD.EXE
    • Coming from PowerShell
    • Nu map from other shells and domain specific languages
    • Nu Map from Imperative Languages
    • Nu Map from Functional Languages
    • Nushell operator map
  • Design Notes
    • How Nushell Code Gets Run
  • (Not So) Advanced
    • Standard Library
    • Dataframes
    • Metadata
    • Creating Your Own Errors
    • Parallelism
    • Plugins
    • explore
    • Building TUIs with tui
    • MCP Server

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 merge command:

    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.

Edit this page on GitHub
Contributors: NotTheDr01ds, fdncred, Hofer-Julian, heinwol, 0x4D5352, joshuanussbaum, Jan Klass
Prev
Working with Lists
Next
Working with Tables