Skip to main content
Lists and Collections
Updated over 8 months ago

If your problem involves finding or comparing a number of things in Neara, you will likely take advantage of lists and collections.

Some examples of when lists and collections are used:

  • Building a report of all the spans that cross other spans, retrieving the distance between the top and bottom crossing spans in various environments, as well as properties of those spans. Here the user would build a list of spans that are below other spans, then query those list items for their various properties, locations, etc.

  • Building a report of all cross-arms that are supporting HV lines to analyse stresses, bending moment etc, on those cross-arms. The user may create a list of all cross-arms on a pole, filtered by a minimum voltage of the spans attached to each cross-arm.

  • Identifying the direction of wind that causes the highest stress on a pole. The user would create a list of environments on the pole, then query each item in that list, returning the properties of the environment for which the pole has highest stress.

This article explains the composition and uses of both concepts.


Lists

Lists are one of the more powerful formulaic concepts in Neara. You can create lists of objects / items of the same "type".

The simplest form of a list can be created using the list() function. Note that all items in this list are of type Text:

The type will be displayed in the field settings menu above the formula definition, as well as in the schema editor, as List<something>. The items in the following list are of type Integral :

You will also encounter lists of more exotic types, like lists of poles, spans, strain sections, environments, etc.

Some of these are accessible as in-built fields to objects in the model schema. For example, on a poles report / from a pole object, you have access to a field called spans, which returns a list of all the spans attached to that pole:

Various in-built functions also return lists. For example, find_nearby_poles(<object>, <distance>) returns a list of nearby poles:

Other formulas to explore include:

  • find_nearby_spans

  • find_spans_below

  • find_crossing_spans_below

Searching for the word find in the schema editor's documentation section will reveal other similar functions:

More list-related functions can be found in the second part of this article.

Filtering lists

Given a list of objects, you can use various functions to return a reduced list of objects. A very common function for this purpose is filter()

Filter takes two inputs: a list of items, and a "match" - a list of boolean values (True or False) of the same length as the first list. It will then return the items in the first list that align with a True value in the second list.

Visualising the operation like this may help with intuition:

You can create the second list by performing a logical operation over a list. For example, given a list of numbers, I want only numbers higher than 10:

1. Define the list

2. Define the match

3. Use filter to receive the expected, filtered list of numbers

You can may want to condense this operation by defining the match directly in the filter function as follows:

Accessing values of list items

After defining your list, it is possible to access the properties of individual items in the list. This is most simply demonstrated using the in-built lists that connect objects in the schema, like the field spans on a pole report.

To access properties of the items of a list, use the [] sign. For example:

  • On a span report, pole1.height will return a single value, the height of a pole

  • On a pole report, spans[].length will return a list of values, the lengths of the spans attached to the pole

  • One application of this is to help filter lists to just the item or items of interest. For example, on a pole report, the following formula will return a list of spans attached to this pole that are over 40m long

Note that the [] sign can chain together to access items within items. On a pole report, writing the formula spans[].MidCableObjects[].type will return a list of all the types of all the Mid Cable Objects of all the spans attached to a given pole.


Collections

When you make a variable of a List<> type in Neara, you automatically also create a Collection. A Collection is an extension of the pole's schema, defined by the list, and is a powerful feature in Neara's formula language.

Simple example

On a pole report, create a new custom field called u_my_simple_list defined as follows:

Note that there are three poles in the design, so there are three rows in the pole report.

You can access the collection that u_my_simple_list has just defined by opening the report configuration (click the “gear” icon in top right corner) and select c_my_simple_list from the drop-down menu:

The report is now no-longer a pole report, but a c_my_simple_list report. Notice that each row corresponds to one item in the u_my_simple_list column of the poles report:

A way to understand this is to imagine the collection as the result of combining all the items in all the cells of the u_my_simple_list column, then creating a row for each item in that combined list in this new report.

The following colour-coding may help visualize the operation:

Advanced example

To explore some of the more advanced concepts, we'll make a slightly more complex collection on the pole report.

Start by turning the report back into a pole report in the report configuration, as described above.

The following formula will return all the spans on each pole for which the span length is > 40m:

If your project contains spans of over 40m length, when you navigate into that collection (via the report configuration) you will see a number of rows:

Collections come with a number of built-in fields:

Built-in field

How it is presented in this example

The parent item that the collection was built on

In this case: pole – because we defined the collection on the poles object

The items that are listed, which will be named with the suffix _item

In this case, spans_over_40m_item because original list we defined was of type List<Span>

Is Selected, which is available if the listed items are selectable in the perspective view

A boolean value that is true if the item is currently selected.

In this case, the items that are listed are spans, which are selectable in the perspective view.

This is not so for the first collection we defined above, as you can't "click on" "A" in the perspective view.

Is Attached To Selected

Similar to above but detects whether attached / parent objects are currently selected in perspective view

Accessing parent properties from the collection object

The built-in links to the parent item (in this case pole) and the list item (in this case spans_over_40m_item) allow you to access the properties of those objects.

For example, we can get the ground clearance of each span as follows:

Similarly we can access the pole height of the parent pole item as follows:

Accessing a collection's properties via other connected objects

You can access the properties of the collection in the same manner as you can access any in-built schema item properties. The fields are accessible via the c_spans_over_40m field.

For instance, back in the original pole report, we can get a list of the ground clearances for all spans over 40m as follows:

We can then find the minimum ground clearance for all spans attached to a given pole that are over 40m in length as follows:

These examples demonstrate the power of custom collections in Neara:

  1. First we identified a list of items of interest that relate in some way to our poles i.e. “find me all long spans for each pole”

  2. Then we went into that collection and performed some calculations and queries on a per-collection-item basis i.e. “for each long span for each pole, give me the ground clearance”

  3. Finally, we went back to poles, queried the custom fields in the collection, and returned the lowest ground clearance.

Querying the item or collection

If you define a collection of type List<Span>, you have the option to either access the collection, with any custom fields you have added onto the collection, or directly access the span schema itself.

For example, you may define a custom field called u_long_spans on a pole report with the formula filter(spans, spans[].length > 50m), which will have the type List<Spans>.

  • You can directly access the spans schema via u_long_spans[], e.g. to retrieve the length of the spans using the formula u_long_spans[].length. Note the use of u_long_spans instead of c_long_spans.

  • It is possible to return the exact same list of lengths via the collection using this formula: c_long_spans[].long_spans_item[].length.

  • The collection is not the item itself. For the example above, any fields made in the c_long_spans report will not exist on the spans object, they will only exist in the collection.

You may decide to define fields on the collection as opposed to the span object itself for a number of reasons:

  • Your final report of interest may be the report of the collection, not a span report, in which case defining the formulas for the columns of your report on the collection makes more sense than referencing them via the collection's _item object.

  • The fields you are defining are not universally applicable to all spans, but are instead only related to the filtered spans that exist in this collection. In this case, defining fields on the collection will prevent them from "polluting" the span object with largely unnecessary custom fields.


Functions

The following functions can be used to create or manipulate lists and collections in Neara.

range

range(n)

or

range(start, end, [step])

Generates a list of whole numbers [0, n) if a single argument is provided; or generates a list of whole numbers ["start", "end") if two arguments are provided.

Optionally, step size can be defined as a third argument.

End must be greater than start, step size must be positive.

Generates up to 1000 items.

  • range(8) returns [0, 1, 2, 3, 4, 5, 6, 7, 8]

  • range(3, 8) returns [3, 4, 5, 6, 7]

  • range(3, 8, 2) returns [3, 5, 7]

map

map(key, value, [key, value, ...])

Builds a map object, which can then be passed to functions like map_lookup().

Takes an even number of arguments, in each pair there is a key and a value. The keys must all be of a compatible type with each other, and likewise the values with each other.

  • map("a", 5, "b", 6) returns two key-value pairs {a: 5, b: 6}

  • map(5, "a", 6, "b") returns two key-value pairs {5: a, 6: b}

map_lookup

map_lookup(map, key, [default])

Looks up a value from a map object. If the key is not in the map, returns the given default value if specified or null if not specified. The key must match the type of the keys in the map.

  • map_lookup(map("a", 5, "b", 6), "a") produces 5

  • map_lookup(map("a", 5, "b", 6), "z", 7) produces 7

map_keys

map_keys(map)

Returns all keys for the given map.

  • map_keys(map("a", 5, "b", 6)) returns the list of keys [a, b]

map_values

map_values(map)

Returns all values for the given map.

  • map_values(map("a", 5, "b", 6)) returns the list of values [5, 6]

list

list([a, b, ...])

Creates a list of values. The values must all be of the same type.

  • list("a", "b", "c", "d") returns ["a", "b", "c", "d"]

  • list(1, 2, 3, 4) returns [1, 2, 3, 4]

tuple

tuple([a, b, ...])

Creates a tuple/record of values. The values may be of different types.

Unlike lists, tuples have a type that encodes the information of each field.

Therefore, tuples are compatible with each other if they have correspondingly typed elements.

Lists, on the other hand, are compatible regardless of their length, as long as all their elements are the same type.

len

len(str|list)

Returns the length of a string or list of values.

  • len("string") returns 6

  • len(list(1, 2, 3, 4, 5) returns 5

index

index(list, index)

Returns the nth item from the start of the list or null if indexing past the list.

  • index(list, 0) returns the first item in the list

  • index(list, len(list) - 1) returns the last item in the list

index_of

index_of(list, object)

Returns the index of the first matching object in the list or -1 if not found

  • index_of(list(4,1,3), 1) returns 1

  • index_of(list(4,1,3), 4) returns 0

  • index_of(list(1,4,4), 4) returns 1

  • index_of(list("a", "b", "c"), "d") returns -1

max_by

max_by(list, comparison_value_list)

Returns the item from the list that corresponds with the maximum value in the comparison value list.

  • max_by(model().Poles[].label, model().Poles[].lean_angle) returns the label of the pole with the greatest lean angle

min_by

min_by(list, comparison_value_list)

Returns the item from the list that corresponds with the minimum value in the comparison value list.

  • min_by(model().Spans[].label, model().Spans[].ground_clearance) returns the label of the span with the lowest ground clearance.

flatten

flatten(list_of_lists)

Flattens a list of lists.

  • flatten(list(1, 2), list(3, 4)) returns [1, 2, 3, 4]

filter

filter(list, match)

Filters a list. If the list contains structured objects, the match should be a map of field->value.

  • filter(model().Poles, model().Poles[].type = "wood") returns a list of pole objects of type “wood”

  • filter(model().Poles[].label, model().Poles[].lean_angle > unit_value(5,"degrees")) returns a list of pole labels with a lean angle greater than 5 degrees

  • filter(model().Spans, model().Spans[].ground_clearance < 3m) returns a list of all span objects that have a ground clearance of less than 3m

find

find(list, match)

Finds a single item in a list.

Equivalent to index(filter(list, match), 0).

  • find(model().Poles, model().Poles[].label = "Pole 1234") returns the first pole object that has the label “Pole 1234”

group_by

group_by(list, group_value)

Returns a Map(group, items) with one entry per unique group.

unique

unique(list)

Removes repeated items from the list.

  • unique(list(1, 2, 3, 1, 3) returns [1, 2, 3]

sort

sort(list)

Returns a list with the items sorted

  • sort(list(2, 1, 3, -5)) returns a numerically ascending list [-5, 1, 2, 3]

  • sort(list("b", "c", "a", "d") returns an alphabetically ascending list ["a", "b", "c", "d"]

sort_by

sort_by(list, comparable_values, [order: "asc" | "desc", nulls_first: true])

Returns a list with the items sorted by [comparable_values].

If [nulls_first] is true, then null values will appear at the start of the list, otherwise they will appear at the end.

If [order] is "asc", then non-null values are sorted in ascending order, or "desc" for descending order.

Note that [nulls_first] will always be respected regardless of the value of [order].

  • sort_by(model().Poles[].label, model().Poles[].lean_angle, order: "desc") returns a list of pole labels ordered from largest lean angle to smallest lean angle

intersect

intersect(list1, list2)

Returns a new list consisting of elements contained in both list1 and list2.

  • intersect(list(1, 2, 3, 4), list(3, 4, 5, 6)) returns the list [3, 4]

exclude

exclude(list1, list2)

Returns a new list consisting of all elements in list1 that are not in list2.

  • exclude(list(1, 2, 3, 4), list(3, 4, 5, 6)) returns the list [1, 2]

symmetric_exclude

symmetric_exclude(list1, list2)

Returns a new list consisting of all elements that are only available in either list1 or list2, but not both.

Equivalent to exclude(flatten(list1, list2), intersect(list1, list2)).

  • symmetric_exclude(list(1, 2, 3, 4), list(3, 4, 5, 6)) returns the list [1, 2, 5, 6]

make_props

make_props(key, value, [key, value, ...])

Builds a key-value properties object, which can then be passed to functions like get_prop. Unlike a map, the keys of a properties object must be text, but the values can be of different types. This makes a properties objects similar to a JSON object.

  • make_props("material", "wood", "phases", 3) returns two key-value pairs {"material":"wood","phases":3}

get_prop

get_prop(properties, field, type)

Retrieves a field from the given properties as the specified type. Field must be text, or a list of text to access nested properties.

  • get_prop(gis_properties, "feeder", "text")

  • get_prop(gis_properties, "in_scope", "boolean")

  • get_prop(gis_properties, "num_phases", "integer")

  • get_prop(gis_properties, "safety_factor", "real")

  • get_prop(gis_properties, "length", "m")

Type may be one of text, boolean, integer, real.

Did this answer your question?