WOODY'S
FINDINGS

What's new in Scout 2.0.0?

Checkout the project page

The examples will be given for the following JSON, available in the Playground folder (you might want to download it to keep it in sight). The same commands are available for Plist and XML data.

{
  "people": {
    "Tom": {
      "height": 175,
      "age": 68,
      "hobbies": [
        "cooking",
        "guitar"
      ]
    },
    "Robert": {
      "height": 181,
      "age": 23,
      "hobbies": [
        "video games",
        "party",
        "tennis"
      ],
      "running_records": [
        [
          10,
          12,
          9,
          10
        ],
        [
          9,
          12,
          11
        ]
      ]
    },
    "Suzanne": {
      "job": "actress",
      "movies": [
        {
          "title": "Tomorrow is so far",
          "awards": "Best speech for a silent movie"
        },
        {
          "title": "Yesterday will never go",
          "awards": "Best title"
        },
        {
          "title": "What about today?"
        }
      ]
    }
  }
}
 

Get information about the group

Array/Dictionary count (starting from Scout 1.2.3)

Get an array or a dictionary count using the count | [#] element.
<form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

scout read -i People.json "people.Suzanne.hobbies[#]" # output 3
scout read -i People.json "people[#]" # output 3
<p class="form-language-alternative-label">Swift</p>
json.get("people", "Suzanne", "movies", PathElement.count).int // output 3
json.get("people", PathElement.count).int // output 3

Dictionary keys

Get a dictionary's keys as an array using the keysList | {#} element. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

scout read -i People.json "people{#}"
<p class="form-language-alternative-label">Swift</p>
json.get("people", PathElement.keysList) // new json with an array value

Useful combined with the --csv command (see below) when you want to iterate over the keys of a dictionary.
keys=(`scout read -i People.json "people{#}" —-csv-sep " "`)
for key in $keys;  do
    scout read -i People.json "people.$key";
done

Get a group sample

Array slice

Slice an array with the slice(:) | [lower:upper] element.
Swift
Use .first and .last to specify the first and last possible indexes in the array. They will be resolved at "slicing" time when the array is exposed.
Command-line
Omit the first value to target the first index, and omit the last value to target the last index. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

# get Robert first two hobbies
scout read -i People.json "people.Robert.hobbies[0:1]"
# get the title of the first two Suzanne's movies
scout read -i People.json "people.Suzanne[:1].title"
<p class="form-language-alternative-label">Swift</p>
let bounds = Bounds(lower: .first, upper: 1)
// get the first two movies then their titles
json.get("people", "Suzanne", "movies", PathElement.slice(bounds))
json.get("people", "Suzanne", "movies", PathElement.slice(bounds), "title")

Negative bounds

Use negative bounds to count from the last index. Find more details in the wiki. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

# get Robert last two hobbies
scout read -i People.json "people.Robert.hobbies[-1:]"
# get the title of the first two Suzanne's movies
scout read -i People.json "people.Suzanne[-2:-1].title"
# get all Arnaud's hobbies except the last one
scout read -i People.json "people.Arnaud.hobbies[:-1]"
<p class="form-language-alternative-label">Swift</p>
// get the last two movies
json.get("people", "Suzanne", "movies", PathElement.slice(Bounds(lower: -1, upper: .last)))
// get the first two movies
json.get("people", "Suzanne", "movies", PathElement.slice(Bounds(lower: -2, upper: -1)))
// get all the movies except the last one
json.get("people", "Suzanne", "movies", PathElement.slice(Bounds(lower: .first, upper: -1)))

Dictionary filter

Filter the keys of a dictionary with a regular expression using the filter(:) | #expression# element. A key is valid when its overall content is a match found by the regular expression.
Command-line
Escpape the sharp sign # with \ to use it in the expression, as the expression is enclosed with sharp signs. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

# get Tom and Robert hobbies
scout read -i People.json "people.#Tom|Robert#.hobbies"
# get all the keys starting with "h" in Tom's dictionary: height, hobbies
scout read -i People.json "people.Tom.#h.*#"
<p class="form-language-alternative-label">Swift</p>
// get Tom and Robert hobbies
json.get("people", PathElement.filter("Robert|Tom"), "hobbies")
// get all the keys starting with "h" in Tom's dictionary: height, hobbies
json.get("people", "Tom", PathElement.filter("h.*"))

Mixing

It's possible to mix array slicing and dictionary filtering. For instance to get the first two hobbies of Robert and Tom. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

scout read -i People.json "people.#Tom|Robert#.hobbies[:1]"
<p class="form-language-alternative-label">Swift</p>
let bounds = Bounds(lower: 0, upper: 1)
json.get("people", PathElement.filter("Tom|Robert"), "hobbies", PathElement.slice(bounds))


Output

Folding

You can fold arrays and dictionaries at a certain depth level, which is useful when outputting large data in the terminal. This feature modifies the data so it should only be used for display purposes.

Use the --level | -l option to fold the data (PathExplorer.fold() for Swift). The value is the level at which the group values should be folded.

scout read -i People.json -l 2
should output:
{
  "people" : {
    "Tom" : {
      "hobbies" : [...],
      "age" : 68,
      "height" : 175
    },
    "Robert" : {
      "running_records" : [...],
      "hobbies" : [...],
      "age" : 23,
      "height" : 181
    },
    "Suzanne" : {
      "job" : "actress",
      "movies" : [...]
    }
  }
}

CSV export

You can output an array or a dictionary of arrays as CSV, with the default separator ';' or a custom one.

Array

The command <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

scout read -i People.json "people.Suzanne.movies" --csv
<p class="form-language-alternative-label">Swift</p>
try json.get("people", "Suzanne", "movies").exportCSV()

shoult output
awards;title
Best speech for a silent movie;Tomorrow is so far
Best title;Yesterday will never go
NULL;What about today?

Use the --csv-sep option to choose the separator for the Command-line (default parameter in Swift).

Dictionary of arrays

It's also possible to export a dictionary of arrays as mentioned above. In this case, the first element of each line is the name of the key associated to the array.

scout read -i People.json "people.#Tom|Robert#.hobbies" --csv
will output:
Tom_hobbies;cooking;guitar
Robert_hobbies;video games;party;tennis

Note that the CSV can be exported with the --output | -o command and that the read command now also has this option.


Deleting

Group sample

The group sample features mentioned above can be used to delete several values at once. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

# delete Robert first two hobbies
scout delete -i People.json "people.Robert.hobbies[0:1]"
# delete the title of the first two Suzanne's movies
scout delete -i People.json "people.Suzanne[:1].title"
<p class="form-language-alternative-label">Swift</p>
let bounds = Bounds(lower: 0, upper: 1)
// delete the first two movies or their "title" key
json.delete("people", "Suzanne", "movies", PathElement.slice(bounds))
json.delete("people", "Suzanne", "movies", PathElement.slice(bounds), "title")

<form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>
# delete Tom and Robert hobbies
scout delete -i People.json "people.#Tom|Robert#.hobbies"
# delete all the keys starting with "h" in Tom's dictionary: height, hobbies
scout delete -i People.json "people.Tom.#h.*#"
<p class="form-language-alternative-label">Swift</p>
// delete Tom and Robert hobbies
json.delete("people", PathElement.filter("Robert|Tom")).hobbies
// delete all the keys starting with "h" in Tom's dictionary: height, hobbies
json.delete("people", "Tom", PathElement.filter("h.*"))

Delete if empty

When an array or a dictionary is left empty because all its values have been deleted, it's possible to specify that it should be deleted too.
Command-line
Use the --recursive | -r flag to delete a group value when left empty. <form class="form-language"> <label> <input class="language-input input-cl" type="radio" value="cl" name="language" checked> <span class="input-label-text">Command-line</span> </label> | <label> <input class="language-input input-swift" type="radio" value="swift" name="language"> <span class="input-label-text">Swift</span> </label> </form> <p class="form-language-alternative-label">Command-line</p>

# delete Tom hobbies key as it only has two keys
scout delete -i People.json "people.Tom.hobbies[:1]" -r
# remove the key hobbies from Robert as we are deleting all its keys
scout delete -ir People.json "people.Robert[:]"
<p class="form-language-alternative-label">Swift</p>
let bounds = Bounds(lower: 0, upper: 1)
// delete the hobbies key from Tom as it only has two elements
json.delete("people", "Tom", "hobbies", PathElement.slice(bounds), deleteIfEmpty: true)
// delete the key hobbies from Robert as we are deleting all its keys
let bounds = Bounds(lower: .first, upper: .last)
let path = Path(pathElements: "people", "Robert", "hobbies", .slice(bounds))
json.delete(path, deleteIfEmpty: true)


Miscellaneous

Auto-completion

You can use the auto-completion for the scout commands by running scout install-completion-script. Then source ~/.zshrc or source ~/.bashrc depending on your shell. More information here.

No color

You can use the --nc flag to prevent syntax highliting in the terminal, which is a shorter version of --no-color. This is required when you pipe the output to another command, otherwise the terminal modifier strings will be included.

Verbose flag Breaking change

The verbose flag --verbose | -v has been removed. The default behavior is now to output the data unless the --ouput | -o or --modify | -m option is specified (i.e. the program should write the output in a file).