“For Each”

Outlines are closely related to JavaScript arrays, and they often have a one-to-one correspondence. For example, a simple shopping list outline is nothing more than an array of items expressed vertically in sequence. And so it stands to reason that JavaScript functions designed to iterate arrays would be useful in constructing OmniOutliner outline documents.

The forEach() method calls a provided function once for each element in an array, in order. This method is perfectly designed for constructing outlines. Note: forEach() does not execute the function for array elements without values.

For example, in the following script, the forEach() method is applied to the array of children of the rootItem. Note that the method’s processing function is passed each of the children, one-at-a-time, represented by the pass-through variable titled: “child”

The forEach() Method


rootItem.children.forEach(function(child){//function code})

The function code can then be applied to the passed “child” object.

Let’s use the forEach() method to clear the current contents of our outline document.

DO THIS ►

Copy and paste the following script into the console window, and execute it by pressing the Return key.

omnioutliner:///omnijs-run?script=rootItem%2Echildren%2EforEach%28function%28child%29%7Bchild%2Eremove%28%29%7D%29
Clear the Outline
 

rootItem.children.forEach(function(child){child.remove()})

The script will iterate the list of the top-level outline items and delete each instance by applying the remove() function to it. The result will be a blank outline document.

blank-doc

Array to Outline

Now that the document has been cleared, let’s use the forEach() method to construct a simple single-level outline by iterating an array of planet names and creating a new outline entry for each one.

In the following example, note how the content of the created items is set by appending the topic property to the method, and then assigning it a value. This single-line technique (line 3) is perfect for those situations where you don’t need to store a reference to the newly created item.

DO THIS ►

Copy and paste the following script into the console window, and execute it by pressing the Return key.

planets = ['Mercury','Venus','Earth','Mars','Jupiter','Saturn','Uranus','Neptune','Pluto'] planets.forEach(function(planet){ rootItem.addChild().topic = planet })
omnioutliner:///omnijs-run?script=planets%20%3D%20%5B%27Mercury%27%2C%27Venus%27%2C%27Earth%27%2C%27Mars%27%2C%27Jupiter%27%2C%27Saturn%27%2C%27Uranus%27%2C%27Neptune%27%2C%27Pluto%27%5D%0Aplanets%2EforEach%28function%28planet%29%7B%0A%09rootItem%2EaddChild%28%29%2Etopic%20%3D%20planet%0A%7D%29
Array to Outline
 

planets = ['Mercury','Venus','Earth','Mars','Jupiter','Saturn','Uranus','Neptune','Pluto'] planets.forEach(function(planet){ rootItem.addChild().topic = planet })

The result of the script will be a simple outline of top-level items:

simple-outline

As you can see, it’s easy to create a simple outline from a single array of data.

Next, we’ll use the forEach() method to create a more complex outline, one with multiple levels of entries.

Before creating the new outline, clear the existing entries.

DO THIS ►

Copy and paste the following script into the console window, and execute it by pressing the Return key.

Clear the Outline
 

rootItem.children.forEach(function(child){child.remove()})

The outline is now cleared. For the next example, we’ll use data from two arrays to create a multi-level outline.

The “item index” Parameter

The forEach() method takes a processing function as its sole parameter. By default, the function must include a pass-through variable that will contain a reference to the array item being processed. However, there are two other optional parameters for the function that can be represented with variables:

Optional forEach() Function Parameters


array.forEach(function(item, itemIndex, array){ // function code} )

Using the optional item index pass-through, we can combine data from two aligned arrays to create an outline. Arrays are said to be “aligned” when they are of the same length and their data is related and in the same order.

The following example uses two arrays, a list of the planets, and a list of their moons. Both arrays are of the same length, with related content in the same order.

omnioutliner:///omnijs-run?script=planets%20%3D%20%5B%27Mercury%27%2C%27Venus%27%2C%27Earth%27%2C%27Mars%27%2C%27Jupiter%27%2C%27Saturn%27%2C%27Uranus%27%2C%27Neptune%27%2C%27Pluto%27%5D%0Amoons%20%3D%20%5B%5B%5D%2C%5B%5D%2C%5B%22Moon%22%5D%2C%5B%22Deimos%22%2C%20%22Phobos%22%5D%2C%5B%22Ganymede%22%2C%22Callisto%22%2C%22Io%22%2C%22Europa%22%5D%2C%5B%22Titan%22%2C%22Rhea%22%2C%22Iapetus%22%2C%22Dione%22%2C%22Tethys%22%2C%22Enceladus%22%5D%2C%5B%22Titania%22%2C%22Oberon%22%2C%22Umbriel%22%2C%22Ariel%22%2C%22Miranda%22%5D%2C%5B%22Triton%22%2C%22Proteus%22%5D%2C%5B%22Charon%22%2C%22Hydra%22%2C%22Nix%22%5D%5D%0A%0Aplanets%2EforEach%28function%28planet%2Cindex%29%7B%0A%09item%20%3D%20rootItem%2EaddChild%28null%2Cfunction%28child%29%7B%0A%09%09child%2Etopic%20%3D%20planet%0A%09%7D%29%0A%09moons%5Bindex%5D%2EforEach%28function%28moon%29%7B%0A%09%09item%2EaddChild%28%29%2Etopic%20%3D%20moon%0A%09%7D%29%0A%7D%29
Create Outline from Aligned Arrays
 

planets = ['Mercury','Venus','Earth','Mars','Jupiter','Saturn','Uranus','Neptune','Pluto'] moons = [[],[],["Moon"],["Deimos", "Phobos"],["Ganymede","Callisto","Io","Europa"],["Titan","Rhea","Iapetus","Dione","Tethys","Enceladus"],["Titania","Oberon","Umbriel","Ariel","Miranda"],["Triton","Proteus"],["Charon","Hydra","Nix"]] planets.forEach(function(planet, index){ item = rootItem.addChild(null,function(child){ child.topic = planet }) moons[index].forEach(function(moon){ item.addChild().topic = moon }) })

 1  Source Array • The main array is a list of the planets (yes, I include Pluto in that list!)

 2  Aligned Array • A second array comprised of the names of the larger moons of each planet (Did you know that Jupiter has over 60 moons?). This list is in the same order as the source list.

 4-11  forEach() method • Iterating the items of the source array using a processing function that has the required array item variable, as well as the option array item index pass-through variable.

 5-7  addItem() method • A new child of the rootItem is created and its value set using the passed-through item (planet) of the source array containing the names of the planets.

 8-10  forEach() method • A second forEach() method is used to create children for the previously created child. Notice that the passed-through array item index (index) is used to get the names of the corresponding moons from the secondary moons array. Also note that the planet child (moon) is created and its value set using a single-line of code  9  by appending the topic property to creation method and then setting its value. (Very clever!)

DO THIS ►

Copy and paste the previous script into the console window, and execute it by pressing the Return key.

The result of the script will be the multi-level outline shown below. (I’ve expanded two of the top-level items to reveal their children.)

planets-and-moons--01

Object Data to Outline

In the previous example a multi-level outline was created using two data sources (arrays). In the following example, we’ll examine how to create a multi-level outline from a single data source that is a complex JavaScript object.

First, clear the outline of its existing content.

Clear the Outline
 

rootItem.children.forEach(function(child){child.remove()})

Using JSON (JavaScript Object Notation), data can be stored in either arrays or objects. A JavaScript object is a record of information, represented using an identifying key with a corresponding a data value.

JavaScript objects are placed within curly brackets, with their key and value delimited by a colon ({key:value}). You’ll find that key/value pairing is often used when dealing with stored data.

The following script example demonstrates how to extract the data from a complex JavaScript object and use it to create a multi-level outline in OmniOutliner. In this example, each key is unique — not matching any other key in the parent object. However, it is also common to use the same key for multiple objects, as we’ll examine in another example later in this section.

omnioutliner:///omnijs-run?script=planetData%20%3D%20%7B%22Mercury%22%3A%5B%5D%2C%22Venus%22%3A%5B%5D%2C%22Earth%22%3A%5B%22Moon%22%5D%2C%22Mars%22%3A%5B%22Deimos%22%2C%20%22Phobos%22%5D%2C%22Jupiter%22%3A%5B%22Ganymede%22%2C%22Callisto%22%2C%22Io%22%2C%22Europa%22%5D%2C%22Saturn%22%3A%5B%22Titan%22%2C%22Rhea%22%2C%22Iapetus%22%2C%22Dione%22%2C%22Tethys%22%2C%22Enceladus%22%5D%2C%22Uranus%22%3A%5B%22Titania%22%2C%22Oberon%22%2C%22Umbriel%22%2C%22Ariel%22%2C%22Miranda%22%5D%2C%22Neptune%22%3A%5B%22Triton%22%2C%22Proteus%22%5D%2C%22Pluto%22%3A%5B%22Charon%22%2C%22Hydra%22%2C%22Nix%22%5D%7D%0Aplanets%20%3D%20Object%2Ekeys%28planetData%29%0Aplanets%2EforEach%28function%28planet%29%7B%0A%09row%20%3D%20rootItem%2EaddChild%28%0A%09%09null%2C%0A%09%09function%28child%29%7Bchild%2Etopic%20%3D%20planet%7D%0A%09%29%0A%09moons%20%3D%20planetData%5Bplanet%5D%0A%09moons%2EforEach%28function%28moon%29%7B%0A%09%09row%2EaddChild%28%29%2Etopic%20%3D%20moon%0A%09%7D%29%0A%7D%29
Outline from Object Data
 

planetData = {"Mercury":[],"Venus":[],"Earth":["Moon"],"Mars":["Deimos", "Phobos"],"Jupiter":["Ganymede","Callisto","Io","Europa"],"Saturn":["Titan","Rhea","Iapetus","Dione","Tethys","Enceladus"],"Uranus":["Titania","Oberon","Umbriel","Ariel","Miranda"],"Neptune":["Triton","Proteus"],"Pluto":["Charon","Hydra","Nix"]} planets = Object.keys(planetData) planets.forEach(function(planet){ row = rootItem.addChild( null, function(child){child.topic = planet} ) moons = planetData[planet] moons.forEach(function(moon){ row.addChild().topic = moon }) })

 1  Source Object • The data source for our outline is a JavaScript object containing other objects, each of which contains an array of the names of the larger moons of the planet whose name is used as the key for the object.

 2  Object Keys • Using the keys() method for the Object class to extract the keys for the objects within the main object. In this case, the keys are unique and are the names of the planets. The result is an array of their names matching the order of the secondary objects.

 3-12  forEach() method • Iterating the array of keys (planet names), passing-through the name of a planet with each iteration.

 4-7  addItem() method • Creating a new child of the rootItem and assigning its value to be the name of the planet.

 8  key/value • Using the passed-through planet name as the key for retrieving the value from the corresponding secondary object of the main data object. The retrieved value is an array of the names of the important moons of the planet.

 9-11  forEach() method • Iterating the array of moon names to create and set new children for each of the moons. Note, if the moons array is empty ([]) no items (children) are added.

DO THIS ►

Copy and paste the previous script into the console window, and execute it by pressing the Return key.

The result of the script will be the multi-level outline shown below. (For demonstration, two of the top-level items have been expanded to reveal their children.)

planets-and-moons--01

Array of Keyed Objects

There’s another type of data structure that’s commonly used with JavaScript, and that is an Array of Keyed Objects.

But before examining the new data structure, let’s clear the content from the outline document.

DO THIS ►

Copy and paste the following script into the console window, and execute it by pressing the Return key.

Clear the Outline
 

rootItem.children.forEach(function(child){child.remove()})

In the following example, the parent container in an array that contains multiple objects that implement the key/value design. In this example, each object utilizes the same two keys: “planet” and “moons”. The corresponding value for the “planet” key is the name of the planet, and the corresponding value for the “moons” key is an array of the names of the larger moons of the planet.

omnioutliner:///omnijs-run?script=planetData%20%3D%20%5B%7B%22planet%22%3A%22Mercury%22%2C%22moons%22%3A%5B%5D%7D%2C%7B%22planet%22%3A%22Venus%22%2C%22moons%22%3A%5B%5D%7D%2C%7B%22planet%22%3A%22Earth%22%2C%22moons%22%3A%5B%22Moon%22%5D%7D%2C%7B%22planet%22%3A%22Mars%22%2C%22moons%22%3A%5B%22Deimos%22%2C%20%22Phobos%22%5D%7D%2C%7B%22planet%22%3A%22Jupiter%22%2C%22moons%22%3A%5B%22Ganymede%22%2C%22Callisto%22%2C%22Io%22%2C%22Europa%22%5D%7D%2C%7B%22planet%22%3A%22Saturn%22%2C%22moons%22%3A%5B%22Titan%22%2C%22Rhea%22%2C%22Iapetus%22%2C%22Dione%22%2C%22Tethys%22%2C%22Enceladus%22%5D%7D%2C%7B%22planet%22%3A%22Uranus%22%2C%22moons%22%3A%5B%22Titania%22%2C%22Oberon%22%2C%22Umbriel%22%2C%22Ariel%22%2C%22Miranda%22%5D%7D%2C%7B%22planet%22%3A%22Neptune%22%2C%22moons%22%3A%5B%22Triton%22%2C%22%22%5D%7D%2C%7B%22planet%22%3A%22Pluto%22%2C%22moons%22%3A%5B%22Charon%22%2C%22Hydra%22%2C%22Nix%22%5D%7D%5D%0AplanetData%2EforEach%28function%28dataObject%29%7B%0A%09planet%20%3D%20dataObject%5B%27planet%27%5D%0A%09moons%20%3D%20dataObject%5B%27moons%27%5D%0A%09row%20%3D%20rootItem%2EaddChild%28%0A%09%09null%2C%0A%09%09function%28child%29%7Bchild%2Etopic%20%3D%20planet%7D%0A%09%29%0A%09moons%2EforEach%28function%28moon%29%7B%0A%09%09row%2EaddChild%28%29%2Etopic%20%3D%20moon%0A%09%7D%29%0A%7D%29
Array of Objects to Outline
 

planetData = [{"planet":"Mercury","moons":[]},{"planet":"Venus","moons":[]},{"planet":"Earth","moons":["Moon"]},{"planet":"Mars","moons":["Deimos", "Phobos"]},{"planet":"Jupiter","moons":["Ganymede","Callisto","Io","Europa"]},{"planet":"Saturn","moons":["Titan","Rhea","Iapetus","Dione","Tethys","Enceladus"]},{"planet":"Uranus","moons":["Titania","Oberon","Umbriel","Ariel","Miranda"]},{"planet":"Neptune","moons":["Triton",""]},{"planet":"Pluto","moons":["Charon","Hydra","Nix"]}] planetData.forEach(function(dataObject){ planet = dataObject['planet'] moons = dataObject['moons'] row = rootItem.addChild( null, function(child){child.topic = planet} ) moons.forEach(function(moon){ row.addChild().topic = moon }) })

 1  Source Data • An array of keyed objects, with each object using the same two keys: planet and moons.

 2-12  forEach() method • Iterating the array of keyed objects with each iteration passing through one of the data objects.

 3  planet data • Use the key “planet” to retrieve and store the name of planet object being processed.

 4  moons data • Use the key “moons” to retrieve and store an array of the names of the moons of the planet object being processed.

 5-8  create and set item • Create a new outline row and set its value to the name of the planet.

 9-11  forEach() method • Iterate the list of moons, creating children (if needed) for the new row.

DO THIS ►

Copy and paste the previous script into the console window, and execute it by pressing the Return key.

The result of the script will be the multi-level outline shown below. (I’ve expanded two of the top-level items to reveal their children.)

planets-and-moons--01

Summary

In this section you’ve learned how to create and remove items from an outline using the rootItem object and the addChild() and forEach() methods. In creating outlines you’ve parsed JavaScript arrays and objects with key/value pairs. Many of the scripts you write for yourself will incorporate the techniques demonstrated in this section.

What’s Next?

In the next section, we’ll examine how to use the Editor class to manipulate the items in an outline document.