Get nth element of a collection in Cypher - collections

Using Cypher 1.8, there are some functions working on collections and returning a single element:
HEAD( expression ):
START a=node(2)
RETURN a.array, head(a.array)
LAST( expression ):
START a=node(2)
RETURN a.array, last(a.array)
However, I could not find a function to return the nth element of a collection. What am I missing?

There's no good way to do that at the moment. Please submit a feature request at https://github.com/neo4j/neo4j
I've seen people do head(tail(tail(tail(coll)))), and while it's probably acceptably fast, it still makes me a little ill to see in a query, especially if you're talking about the 17th element or worse.
Example:
http://console.neo4j.org/r/bbo6o4
Update:
Here's a way to do it using reduce and range. It makes it so you can give a parameter for nth at least, even though it still makes me cringe:
start n=node(*)
with collect(n) as allnodes
return head(reduce(acc=allnodes, x in range(1,3): tail(acc)));
http://console.neo4j.org/r/8erfup
Update 2 (8/31/2013):
The new collection syntax is now merged into 2.0 and will be theoretically be a part of M05! So, you'll be able to do:
start n=node(*)
with collect(n) as allnodes
return allnodes[3]; // or slices, like [1..3]
I'll add a link to the snapshot documentation when it gets updated.

I've just come across this old question, and for the benefit of anyone else recently coming across it... it seems the list support has improved.
From the Cypher 4 list docs:
Cypher has comprehensive support for lists.
^ Sidenote: I think that's list comprehensions pun? ;-)
They go on to give an example showing how you'd access the n'th element of a list:
To access individual elements in the list, we use the square brackets again. This will extract from the start index and up to but not including the end index.
... we’ll use the range function. It gives you a list containing all numbers between given start and end numbers. Range is inclusive in both ends.
RETURN range(0, 10)[3]
^ returns "3"

Currently, with the release of APOC Procedures 3.3.0.2 you can use aggregation functions.
This way, you can do thinks like:
create (:Node {node_id : 1}),
(:Node {node_id : 2}),
(:Node {node_id : 3});
match(n:Node)
with n order by n.node_id
// returns {"node_id":2}
return apoc.agg.nth(n, 1);
or:
match(n:Node)
with n order by n.node_id
// returns {"node_id":1}
// you can also use apoc.agg.last
return apoc.agg.first(n);
To work with lists UNWIND the list first:
with ['fist', 'second', 'third'] as list
unwind list as value
// returns 'second'
return apoc.agg.nth(value, 1);

Related

Gremlin continue traversal only if 2 vertices are not the same

I have a query which looks at 2 different vertices and I want to stop traversing if they don't both roll up to the same root ancestor via a path of "contains" edges.
g.V('node1')
.until(hasLabel('root')).repeat(in('contains')).as('node1Root')
.V('node2')
.until(hasLabel('root')).repeat(in('contains')).as('node2Root')
//FILTER|WHERE clause
I'd like to confirm that node1Root and node2root are the same vertex before continuing the traversal, but for the life of me I cannot figure out how to do this.
I've tried the following:
g.V('node1')
.until(hasLabel('root')).repeat(in('contains')).as('node1Root')
.V('node2')
.until(hasLabel('root')).repeat(in('contains')).as('node2Root')
//.where('node1Root', P.eq('node2Root')
//.where(select("node1Root").is(P.eq("node2Root")))
//.where(select("node1Root").is("node2Root"))
What's interesting is that the following query does work to filter appropriately.
g.V('node1').as('1')
.V('node2').as('2')
.where('1', P.eq('2'))
I'm not sure if there's something up with the until/repeat that screws it up or if I'm just doing something blatantly wrong. Any help would be much appreciated.
Thanks!
I found How to check equality with nodes from an earlier part of query in Gremlin?
and it seems like you use "as" with the same key as the previous "as" and if they match its considered equal.
So here's the winner (I think):
g.V('node1')
.until(hasLabel('root')).repeat(in('contains')).as('node1Root')
.V('node2')
.until(hasLabel('root')).repeat(in('contains')).as('node2Root')
.where(select('node1Root').as('node2Root')
//.not(select('node1Root').as('node2Root')) //OR this to determine they aren't the same
//continue traversal
I also found that my original issue was that the .until().repeat() steps could return a LIST, but in my case I know that my graph model will always return a single 'root' so to make it work, I can use 'unfold'
g.V('node1')
.until(hasLabel('root')).repeat(in('contains')).unfold().as('node1Root')
.V('node2')
.until(hasLabel('root')).repeat(in('contains')).unfold().as('node2Root')
.where('node1Root', P.eq('node2Root')
I think I'll be going with the second solution because I'm much more confident in it, unless I hear otherwise.
You can try this gremlin query
g.V(node1-id)
.map(until(hasLabel('root')).repeat(in().aggregate('x')).cap('x')).as("array")
.V(node2-id)
.until(
as("i").select("array").unfold().as("j")
.where("i", eq("j"))
).repeat(in())
Here we are putting all the vertices in path to root from node1 in an array, and secondly we are checking existence of node in array.
this query can only work with traversal with only one iteration because aggregate step collect to a global variable to traversal that means it will be same array for every iteration. To fix this If you are doing this on jvm do use lamda/groovy closures
g.V(node-start-id-1,node-start-id-2)
.map(
{ x->
var v = x.get()
var g = getGraph().get().traversal();
g.V(v.id())until(hasLabel('root')).repeat(in().aggregate('x')).cap('x')).next()
}
)
.as("array")
.V(node2-id)
.until(
as("i").select("array").unfold().as("j")
.where("i", eq("j"))
).repeat(in())

Xquery result duplicated

I'm not getting the output I want. I don't understand why the result is duplicated. Can someone help me?
for $i in 1 to 2
let $rng:=random-number-generator()
let $rng1:=$rng('permute')(1 to 10)
let $rng:=$rng('next')()
let $rng2:=$rng('permute')(1 to 10)
let $rng:=$rng('next')()
let $rng3:=$rng('permute')(1 to 10)
return (string-join($rng1),string-join($rng2),string-join($rng3),",")
result:
23496815107
31018674529
31017684259
23496815107
31018674529
31017684259
The result is duplicated because of the initial for $i in 1 to 2, and because the variable $i is not actually used anywhere.
I edited the query based on your comment (getting 10 numbers). From what I understand, the difficulty here is to chain the calls (alternating between 'next' and 'permute'). Chaining calls can be done with a tail recursion.
declare function local:multiple-calls(
$rng as function(*),
$number-of-times as xs:integer) as item()* {
if($number-of-times le 0)
then ()
else
let $rng := $rng('next')
return ($rng('permute')(1 to 10),
local:multiple-calls($rng, $number-of-times - 1))
};
local:multiple-calls(random-number-generator(), 10)
Note: I am not sure if (1 to 10) is what needs to actually be passed to the call to $rng('permute'), or if it was an attempt to output ten numbers. In doubt, I haven't changed it.
The specification is here:
http://www.w3.org/TR/xpath-functions-31/#func-random-number-generator
It says:
Both forms of the function are ·deterministic·: calling the function
twice with the same arguments, within a single ·execution scope·,
produces the same results.
If you supply $i as the $seed argument to random-number-generator then the two sequences should be different.
I think I now understand what confuses you in this original query. One could indeed expect the random numbers to be generated differently for each iteration of $i.
However, XQuery is (to put it simply, with a few exceptions) deterministic. This means that the random generator probably gets initialized in each iteration with the same, default seed.
Thus, I have a second potential answer:
If you have a way to pass a different seed to $rng, you could slightly modify your initial query by constructing a seed based on $i and maybe current-dateTime() in each iteration before generating the numbers. But it will still be the same if you execute the query several times unless you involve the current date/time.

Tail Recursions in erlang

I'm learning Erlang from the very basic and have a problem with a tail recursive function. I want my function to receive a list and return a new list where element = element + 1. For example, if I send [1,2,3,4,5] as an argument, it must return [2,3,4,5,6]. The problem is that when I send that exact arguments, it returns [[[[[[]|2]|3]|4]|5]|6].
My code is this:
-module(test).
-export([test/0]).
test()->
List = [1,2,3,4,5],
sum_list_2(List).
sum_list_2(List)->
sum_list_2(List,[]).
sum_list_2([Head|Tail], Result)->
sum_list_2(Tail,[Result|Head +1]);
sum_list_2([], Result)->
Result.
However, if I change my function to this:
sum_list_2([Head|Tail], Result)->
sum_list_2(Tail,[Head +1|Result]);
sum_list_2([], Result)->
Result.
It outputs [6,5,4,3,2] which is OK. Why the function doesn't work the other way around([Result|Head+1] outputing [2,3,4,5,6])?
PS: I know this particular problem is solved with list comprehensions, but I want to do it with recursion.
For this kind of manipulation you should use list comprehension:
1> L = [1,2,3,4,5,6].
[1,2,3,4,5,6]
2> [X+1 || X <- L].
[2,3,4,5,6,7]
it is the fastest and most idiomatic way to do it.
A remark on your fist version: [Result|Head +1] builds an improper list. the construction is always [Head|Tail] where Tail is a list. You could use Result ++ [Head+1] but this would perform a copy of the Result list at each recursive call.
You can also look at the code of lists:map/2 which is not tail recursive, but it seems that actual optimization of the compiler work well in this case:
inc([H|T]) -> [H+1|inc(T)];
inc([]) -> [].
[edit]
The internal and hidden representation of a list looks like a chained list. Each element contains a term and a reference to the tail. So adding an element on top of the head does not need to modify the existing list, but adding something at the end needs to mutate the last element (the reference to the empty list is replaced by a reference to the new sublist). As variables are not mutable, it needs to make a modified copy of the last element which in turn needs to mutate the previous element of the list and so on. As far as I know, the optimizations of the compiler do not make the decision to mutate variable (deduction from the the documentation).
The function that produces the result in reverse order is a natural consequence of you adding the newly incremented element to the front of the Result list. This isn't uncommon, and the recommended "fix" is to simply list:reverse/1 the output before returning it.
Whilst in this case you could simply use the ++ operator instead of the [H|T] "cons" operator to join your results the other way around, giving you the desired output in the correct order:
sum_list_2([Head|Tail], Result)->
sum_list_2(Tail, Result ++ [Head + 1]);
doing so isn't recommended because the ++ operator always copies it's (increasingly large) left hand operand, causing the algorithm to operate in O(n^2) time instead of the [Head + 1 | Tail] version's O(n) time.

Returning multiple values in Ruby, to be used to call a function

Is it possible to return multiple values from a function?
I want to pass the return values into another function, and I wonder if I can avoid having to explode the array into multiple values
My problem?
I am upgrading Capybara for my project, and I realized, thanks to CSS 'contains' selector & upgrade of Capybara, that the statement below will no longer work
has_selector?(:css, "#rightCol:contains(\"#{page_name}\")")
I want to get it working with minimum effort (there are a lot of such cases), So I came up with the idea of using Nokogiri to convert the css to xpath. I wanted to write it so that the above function can become
has_selector? xpath(:css, "#rightCol:contains(\"#{page_name}\")")
But since xpath has to return an array, I need to actually write this
has_selector?(*xpath(:css, "#rightCol:contains(\"#{page_name}\")"))
Is there a way to get the former behavior?
It can be assumed that right now xpath func is like the below, for brevity.
def xpath(*a)
[1, 2]
end
You cannot let a method return multiple values. In order to do what you want, you have to change has_selector?, maybe something like this:
alias old_has_selector? :has_selector?
def has_selector? arg
case arg
when Array then old_has_selector?(*arg)
else old_has_selector?(arg)
end
end
Ruby has limited support for returning multiple values from a function. In particular a returned Array will get "destructured" when assigning to multiple variables:
def foo
[1, 2]
end
a, b = foo
a #=> 1
b #=> 2
However in your case you need the splat (*) to make it clear you're not just passing the array as the first argument.
If you want a cleaner syntax, why not just write your own wrapper:
def has_xpath?(xp)
has_selector?(*xpath(:css, xp))
end

How do I make this predicate work? extractedKnowledge(SourceList,ExtractedList)

I have this predicate that I can't seem to get to work.
The predicate should be used the following way: You give the predicate a list of things (KnowledgeList), and an uninstantiated variable (ExtractedList).
The predicate then should proceed filling ExtractedList.
To fill ExtractedList it iterates over the items of KnowledgeList,
builds a new list of extracted things, and appends this list to ExtractedList.
I know I should probably use recursion to solve this, but I feel stumped at the moment.
extractedKnowledge(KnowledgeList, ExtractedList) :-
list(KnowledgeList),
ExtractedList = [],
length(KnowledgeList,ListLength),
for(X,1,ListLength),
nth(X,KnowledgeList,ListElement),
...?
Since you unify ExtractedList which [], it will always be empty. You should indeed use recursion. Here's a skeletal recursive program to get you started:
% base case: we can only extract 0 items from 0 items
extracted_knowledge([],[]).
% recursive case
extracted_knowledge([Item|Knowledge], Extracted) :-
extracted_knowledge(Knowledge, Extracted0),
% build Extracted from Extracted0 by adding Item,
% if it needs to be extracted

Resources