List expressions
List expressions allow you to manipulate and query LIST values in Cypher®.
For more expressions that evaluate to a LIST value, see List functions.
For information about how to check membership in a LIST using the IN operator, see Predicates → List operators.
Example graph
The following graph is used for the examples below:
To recreate the graph, run the following query against an empty Neo4j database:
CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', skills: ['Java', 'Python']}),
(cecil:Person {name: 'Cecil', age: 25, role: 'Software developer', skills: ['Java', 'Python']}),
(cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer', skills: ['JavaScript', 'TypeScript']}),
(charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer', skills: ['C++', 'Python']}),
(daniel:Person {name: 'Daniel', age: 39, role: 'Director', skills: ['Ruby', 'Go']}),
(eskil:Person {name: 'Eskil', age: 39, role: 'CEO', skills: ['Java', 'C++', 'Python']}),
(cecil)-[:WORKS_FOR]->(alice),
(cecilia)-[:WORKS_FOR]->(alice),
(charlie)-[:WORKS_FOR]->(daniel),
(alice)-[:WORKS_FOR]->(daniel),
(daniel)-[:WORKS_FOR]->(eskil)
List element access
The subscript operator, [], can be used to access specific elements in a LIST.
[0] refers to the first element in a LIST, [1] to the second, and so on.
[-1] refers to the last element in a LIST, [-2] to the penultimate element, and so on.
LISTWITH [1, 2, 3, 4] AS list
RETURN list[0] AS firstElement,
list[2] AS thirdElement,
list[-1] AS finalElement
| firstElement | thirdElement | finalElement |
|---|---|---|
|
|
|
Rows: 1 |
||
The index of the element in the LIST can be parameterized.
{
"myIndex" : 1
}
LIST elements with a parameterWITH [1, 2, 3, 4] AS list
RETURN list[$myIndex] AS secondElement
| secondElement |
|---|
|
Rows: 1 |
LIST within a nested LISTWITH [[1, 2], [3, 4], [5, 6]] AS nestedList
RETURN nestedList[1] AS secondList
| secondList |
|---|
|
Rows: 1 |
LISTWITH [[1, 2], [3, 4], [5, 6]] AS nestedList
RETURN nestedList[1] AS secondList,
nestedList[1][0] AS firstElementOfSecondList
| secondList | firstElementOfSecondList |
|---|---|
|
|
Rows: 1 |
|
The index in the list element access can be any expression, including a variable.
WITH [[1, 2], [3, 4], [5, 6]] AS nestedList, 2 AS listIndex
RETURN nestedList[listIndex] AS thirdList,
nestedList[listIndex][listIndex - 1] AS secondElementOfThirdList
| thirdList | secondElementOfThirdList |
|---|---|
|
|
Rows: 1 |
|
The IN operator, which checks for LIST membership, can be used together with [] to test whether an element exists in a nested LIST.
LISTWITH [[1, 2, 3], [4, 5, 6]] AS nestedList
RETURN 3 IN nestedList[0] AS elementPresent
| elementPresent |
|---|
|
Rows: 1 |
Attempting to reference an element outside the bounds of the LIST will return null, as will attempting to access elements from an empty LIST.
LIST accessWITH [1, 2, 3, 4] AS list, [] AS emptyList
RETURN list[5] AS outOfBound, emptyList[0] AS emptyAccess
| outOfBound | emptyAccess |
|---|---|
|
|
Rows: 1 |
|
List slicing
LIST values can be sliced if a range is provided within the subscript operator.
The bounds of the range are separated using two dots (..).
This allows for extracting a subset of a LIST rather than a single element.
List slicing is inclusive at the start of the range, but exclusive at the end (e.g. list[start..end] includes start, but excludes end).
LISTWITH [1, 2, 3, 4, 5, 6] AS list
RETURN list[2..4] AS middleElements,
list[..2] AS noLowerBound,
list[2..] AS noUpperBound
| middleElements | noLowerBound | noUpperBound |
|---|---|---|
|
|
|
Rows: 1 |
||
Negative indexing in list slicing references elements from the end of the LIST; ..-1 excludes the last element, ..-2 excludes the last two elements, and so on.
WITH [1, 2, 3, 4, 5, 6] AS list
RETURN list[..-1] AS finalElementRemoved,
list[..-2] AS finalTwoElementsRemoved,
list[-3..-1] AS removedFirstThreeAndLast
| finalElementRemoved | finalTwoElementsRemoved | removedFirstThreeAndLast |
|---|---|---|
|
|
|
Rows: 1 |
||
When slicing nested LIST values, it is important to specify which level is sliced.
The below example slices the outer LIST and returns the first two nested LIST values.
LISTWITH [[1, 2, 3], [4, 5, 6], [7, 8, 9]] AS nestedList
RETURN nestedList[0..2] AS slicedNestedList
| slicedNestedList |
|---|
|
Rows: 1 |
Slicing inner LIST values require two [] operators; the first [] accesses elements from the outer LIST, while the second slices or accesses elements from the inner LIST.
LISTWITH [[1, 2, 3], [4, 5, 6], [7, 8, 9]] AS nestedList
RETURN nestedList[1][0..2] AS slicedInnerList
| slicedInnerList |
|---|
|
Rows: 1 |
Accessing specific elements or a range of elements can also be used in combination with the + operator to create a new LIST with values inserted into specific sections of an existing LIST value.
LISTWITH [1, 3, 4] AS list
RETURN list[0] + [2] + list[1..] AS newList
| newList |
|---|
|
Rows: 1 |
List concatenation
Cypher contains two list concatenation operators: || and `.
`||` is xref:appendix/gql-conformance/index.adoc[GQL conformant], while ` is not.
|| and +RETURN [1,2] || [3,4] AS list1,
[1,2] + [3,4] AS list2
| list1 | list2 |
|---|---|
|
|
Rows: 1 |
|
LIST propertiesMATCH (cecil:Person {name: 'Cecil'}), (cecilia:Person {name: 'Cecilia'})
RETURN cecil.skills || cecilia.skills AS combinedSkills
| combinedSkills |
|---|
|
Rows: 1 |
If null is part of a concatenated LIST, null will be a part of the new LIST.
LIST including nullRETURN [1, 2] || [3, null] AS listWithNull
| listWithNull |
|---|
|
Rows: 1 |
For removing null values when concatenating LIST values, see null, list concatenation, and list comprehension.
Add elements to a list
The + operator can add elements to the beginning or end of a LIST value.
This is not possible using the || operator.
LISTWITH [1, 2, 3, 4] AS list
RETURN 0 + list AS newBeginning,
list + 5 AS newEnd
| newBeginning | newEnd |
|---|---|
|
|
Rows: 1 |
|
To insert a LIST value into a nested LIST, the added LIST must itself be nested.
If the added LIST is not nested, its elements are treated as individual elements, whereas if it is nested, it maintains the LIST structure within the nested LIST.
LIST values to a nested LISTWITH [[1, 2], [3, 4]] AS nestedList
RETURN nestedList + [5, 6] AS nonNestedAddition,
nestedList + [[5, 6]] AS nestedAddition
| nonNestedAddition | nestedAddition |
|---|---|
|
|
Rows: 1 |
|
LIST propertyMATCH (cecil:Person {name: 'Cecil'})
SET cecil.skills = "Cypher" + cecil.skills
RETURN cecil.skills AS skillsList
| skillsList |
|---|
|
Rows: 1 |
List comprehension
List comprehension is used to create new LIST values by iterating over existing LIST values and transforming the elements based on certain conditions or operations.
This process effectively maps each element in the original LIST to a new value.
The result is a new LIST that consists of the transformed elements.
[item IN list [WHERE predicate] | [expression]]
The iteration step (item IN list) ensures that each element of a list is accessed one by one, while the expression optionally applies a transformation to each item, creating new LIST values containing the modified elements.
RETURN [x IN range(0,10) WHERE x % 2 = 0] AS result
| result |
|---|
|
Rows: 1 |
RETURN [x IN range(0,5) | x * 10] AS result
| result |
|---|
|
Rows: 1 |
WITH [1, 2, 3, 4, 5] AS list
RETURN [n IN list WHERE n > 2 | n] AS filteredList
| filteredList |
|---|
|
Rows: 1 |
The next example shows how to map a LIST using its indexes with a list comprehension.
The range() function is used to generate the indexes from 0 to the last valid index of the LIST, and then each index is combined with its corresponding LIST value into a STRING value.
The result is a LIST of STRING values formatted as 'index: value'.
WITH [1,2,3,4] AS list
RETURN [listIndex IN range(0, size(list)-1) | toString(listIndex) || ': ' || toString(list[listIndex])] AS mappedListElements
| mappedListElements |
|---|
|
Rows: 1 |
The below query iterates over the skills property of each Person node and creates a new LIST by concatenating the STRING " expert" to each element in skills.
LIST properties using list comprehensionMATCH (p:Person) WHERE p.skills IS NOT NULL
ORDER BY p.name
RETURN p.name AS name,
[skill IN p.skills | skill + " expert"] AS modifiedSkills
| name | modifiedSkills |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Rows: 6 |
|
The next query uses the collect() function to gather all Person nodes into a LIST, and the WHERE 'Python' IN person.skills predicate filters that list to include only those nodes whose skills property contains Python.
WHERE predicateMATCH (p:Person)
RETURN [person IN collect(p) WHERE 'Python' IN person.skills | person.name] AS pythonExperts
| pythonExperts |
|---|
|
Rows: 1 |
List comprehension can be used to remove any unknown null values when concatenating LIST values.
null values during list concatenationRETURN [x IN ([1, null, 3] || [null, 5, null]) WHERE x IS NOT NULL] AS listWithoutNull
| listWithoutNull |
|---|
|
Rows: 1 |
Pattern comprehension
Pattern comprehension is used to create new LIST values by matching graph patterns and applying conditions to the matched elements, returning custom projections.
[pattern [WHERE predicate] | expression]
The below query retrieves a list of names of people who work for Alice by using pattern comprehension extract the names of employees into a LIST.
MATCH (alice:Person {name: 'Alice'})
RETURN [(employee:Person)-[:WORKS_FOR]->(alice) | employee.name] AS employees
| employees |
|---|
|
Rows: 1 |
Pattern comprehensions can include WHERE predicates.
WHERE predicateMATCH (alice:Person {name: 'Alice'})
RETURN [(employee:Person)-[:WORKS_FOR]->(alice) WHERE employee.age > 30 | employee.name || ', ' || toString(employee.age)] AS employeesAbove30
| employeesAbove30 |
|---|
|
Rows: 1 |
Pattern comprehension can also match for variable-length patterns. However, pattern comprehension does not support the GQL conformant quantifier syntax.
MATCH (cecil:Person {name: 'Cecil'})
RETURN [(cecil)-[:WORKS_FOR]->+(superior:Person) | superior.skills] AS superiorsSkills
Pattern comprehension only supports only the variable-length relationships syntax.
The below query uses a pattern comprehension to collect the skills of all superiors in the chain above Cecil.
The reduce() function concatenates these skills into a single LIST, and UNWIND is used to flatten this LIST before returning the distinct skills in a new LIST.
MATCH (cecil:Person {name: 'Cecil'})
WITH [(cecil)-[:WORKS_FOR*]->(superior:Person) | superior.skills] AS allSuperiorsSkills
WITH reduce(accumulatedSkills = [], superiorSkills IN allSuperiorsSkills | accumulatedSkills || superiorSkills) AS allSkills
UNWIND allSkills AS superiorsSkills
RETURN collect(DISTINCT superiorsSkills) AS distinctSuperiorsSkills
| distinctSuperiorsSkills |
|---|
|
Rows: 1 |