this week I got this homework to do: count the grade of nodes in a undirected graph and test if there is a euler path in it. the function should work like following:
gradliste([[a,b],[b,c],[b,g],[c,d],[d,e],[e,f],[f,g],[g,h],[c,f]],X).
X = [[a, 1], [b, 3], [c, 3], [g, 3], [d, 2], [e, 2], [f, 3], [h, 1]]
testEulerweg([[a,b],[b,c],[c,d],[d,e],[a,e],[b,d],[b,e],[a,d]]).
true.
my first idea of the functiongradlisteis to 'merge' the graph and generate a list like this:
[a,b,b,c,b,g,c,d,d,e,e,f,f,g,g,h,c,f] then I count numbers of every node. unfortunately I stucked at the merge.
and for the second function testEulerweg I think I should firstly write a function allconnected working like following:
allconnected([[a,b],[b,c],[c,d]]).
true.
allconnected([[a,b],[b,c],[e,f]]).
False.
then I can check if there are none or 2 nodes with odd grade number using the gradliste function.
Can anyone help me on my idea? I'm also open for new ideas:)
thanks in advance
bearzk
The merge function is simple. I'll rename it flatten:
flatten([[X,Y] | Edges], [X,Y|Rest]) :-
flatten(Edges, Rest).
And I'll let you write the base case.
As for finding the Eulerian path, check out the algorithms at Wikipedia. The second one can be easily implemented in terms of select/3, as long as you don't flatten the list first :)
Related
I am browsing through the ruby Array iterators. And I can't find what I am looking for and I think it already exists:
I have two arrays:
["a", "b", "c"]
[0,1,2]
And I want to merge as so:
[ [0, "a"], [1, "b"], [2, "c"] ]
I think the iterator exists in the standard library (I used it before) but I am having trouble finding its name.
This should work:
[0,1,2].zip(["a", "b", "c"]) # => [[0, "a"], [1, "b"], [2, "c"]]
From the official documentation of the Array#zip function:
Converts any arguments to arrays, then merges elements of self with corresponding elements from each argument.
This generates a sequence of ary.size n-element arrays, where n is one more than the count of arguments.
For more info and some other examples, refer to:
https://ruby-doc.org/core-2.4.2/Array.html#method-i-zip
You are looking for the zip function
https://apidock.com/ruby/Array/zip
I think you could use https://apidock.com/ruby/Enumerator/each_with_index
See this post difference between each.with_index and each_with_index in Ruby?
or if you have specific values and you want to map them you would use map or zip. It explains it well in this post
Combine two Arrays into Hash
I trying to set up a scheme for web-clicks, where each node is a (:Click), which links to the click that precedes it by a [:PREV]-edge and the (:Session) that owns it by a [:GEN]-edge. In the end this should happen procedural, a new transaction/insert when a new click is made. While I have no problem generating the involved objects, I cannot figure out how to dynamically select last (:Click) and link it to the current created one.
Generate a session with 2 clicks:
CREATE (s:Session {name:'S0'})
CREATE (c1:Click {name:'C1', click:1}), (c1)<-[:GEN]-(s)
CREATE (c2:Click {name:'C2', click:2}), (c2)<-[:GEN]-(s), (c1)<-[:PREV]-(c2);
generate one other click in separated transaction:
MERGE (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3}),
(c3)<-[:GEN]-(s) //(c2)<-[:PREV]-(c3);
for the commented out link, I cannot use the c2-variable as it is scope-local to the previous transaction.
Now I thought to try something like this to dynamically find the last generated node on the same session and link it
MERGE (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3}), (c3)<-[:GEN]-(s)
MATCH (s)-[:GEN]->(c_prevs:Click)
WITH c_prevs
ORDER BY c_prevs.click DESC LIMIT 1
CREATE (head(c_prevs))<-[:PREV]-(c3)
Unfortunately this won't work for me with any Cypher-construct I came up with so far.
If I understand you can get the last :Click node on the same session this way:
match (:Session {name:'S0'})-[:GEN]->(c:Click)
where not (:Click)-[:PREV]->(c)
return c
That is: Get the node from the same session that does not have an incoming [PREV] relationship. Will return c2
╒═══════════════════════╕
│"c" │
╞═══════════════════════╡
│{"name":"C2","click":2}│
└───────────────────────┘
For your specific case a query like the following should work:
merge (s:Session {name:'S0'})
with s
match (s)-[:GEN]->(last:Click)
where not (:Click)-[:PREV]->(last)
create (c3:Click {name:'C3', click:3}),
(c3)<-[:GEN]-(s),
(last)<-[:PREV]-(c3)
I found the answer to my question to be the following
MATCH (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3})
WITH s, c3
MATCH (s)-[:GEN]->(c_prev:Click)
WITH c_prev, c3, s
ORDER BY c_prev.click DESC LIMIT 1
WITH c_prev, c3, s
CREATE (c_prev)<-[:PREV]-(c3), (c3)<-[:GEN]-(s)
which is chaining through the nodes as variables s, c3 and last_c with the WITH keyword. Unfortunately this involves a lot of repetition, as every WITH in principle is a part-separator in the query, so I learned.
This also allows to carry over already MERGED/CREATED nodes, which might help to ensure their existence.
EDIT:
This problem seems to be even more complicated if clicks should be generated prozedural, thus using one cypher-statement to insert and link any click.
my solution looks like the following
MERGE (s:Session {name: $session_name})
WITH s
CREATE (c:Click {name: $click_name, click: $click_count})
WITH s, c
OPTIONAL MATCH (s)-[:GEN]->(c_prev:Click)
WITH c_prev, c, s
ORDER BY c_prev.click DESC LIMIT 1
WITH c_prev, c, s
FOREACH (o IN CASE WHEN c_prev IS NOT NULL THEN ['1'] ELSE [] END |
CREATE (c_prev)<-[:PREV]-(c)
)
WITH s, c
CREATE (c)<-[:GEN]-(s)
with executing this statement for {$session_name, $click_name, $click_count} =[{'AAA', 'C1', 1}, {'AAA', 'C2', 2}, {'AAA', 'C3', 3}].
Notice that I had to work around the returning empty node-list by explicitly catching this condition and then not executing the subsequent connection statement with the FOREACH-loop on an empty list. This does not only look very ugly, I sincerely think there should be a better way to expressively specify this desired behavior through Cypher in the near future.
Please help me to figure out min_by behaviour.
I have a "normas" table with two columns (raw_value, percentile).
After some calculations I get calculated_result, and my goal is to find percentile for closest raw_value to my calculated_result.
My approach as below:
raw = Norma.where(name: name).map(&:raw_value).min_by { |x| (x.to_f - value.to_f).abs }
It works, but I can't figure out all logic, here's what I mean:
arr = [1,2,3,4,5]
arr.min_by {|x| (x - 3.5).abs}
=> 3
in this case we have two identical differences (0.5 to 3 as well as to 4),
so my question is what is rule for choosing result if more than one minimal found.
Have a productive day! :)
In case of equal values, the first minimum counts.
Try it with [5, 4, 3, 2, 1] and you'll see the result is now 4.
This is consistent with #index which returns the first index position that matches a value.
Think of it as this...
temp_arr = arr.map{ |x| (x-3.5).abs }
arr[temp_arr.index(temp_arr.min)]
The question came up after reading Natural Language Analytics made simple and visual with Neo4j blog entry created by Michael Hunger
When a word is used by more than one sentence (or more than one time in the same sentence), this word will have two or more [NEXT] relationships. In order to know the correct path for each sentence we need to store the segment id and the position id [sid,idx]
Storing one instance is clear, it create an array with two values. But, how do we add two or more arrays? As far as I know, neo4j only accepts basic data types
Instead of using this solution, would it make sense to store one [NEXT] relationship for each sentence path? Of course this would generate a very big amount on relationships
Thanks
NOTE: In the referenced article, there is a typo on the last line of the query in the "I also want to sentence number and word position" section. That is, r.pos = r.pos = [sid,idx] should be: r.pos = r.pos + [sid,idx].
When you use the + operator on 2 collections, you end up with a single collection that merges the contents of the 2 original collections. So, if r.pos starts out as [1, 2], then r.pos + [3, 4] will produce: [1, 2, 3, 4].
Therefore, the article does not an "array of arrays" problem.
I have an array #a = [[9, 15], [], []]
I need to make it [9,15] using map method. How it is possible?
I have tried the below statement,
#a.map{|array| array.collect{|element| element} if array.any?}.compact
But giving [[9,15]] as output. Can anyone just help me out. Thanks :)-
I'm not sure about the requirements, but this gives the desired result
[[9, 15], [], []].flatten