How to amend object in an array with duplicate attributes? (XY coordinates) - arrays

I have an array of objects (nodes) with XY coordinates. Wherever I find a node overlaying another node (with the exact same XY coords) I would like to edit the Y attribute "up" by 3.
id, x, y are all attributes of the objects I am looking at. And collectively held in an Array of nodes.
I am looking to go through the array and whenever a duplicate XY is present, edit the first non-unique instance by adding 3 to the Y attribute. If another duplicate is found, I would like this object's Y attribute to be altered by 6 and so on.
E.g.
NODE X Y
node1 267555 666777
node2 267555 666777
node3 245698 656400
node4 267555 666777
I would like node 2 and node 4 to become:
NODE X Y
node1 267555 666777
node2 267555 666780
node3 245698 656400
node4 267555 666783
Essentially adding 3 to 'Y' for every instance of a duplicate XY, and doing so for every instance where there are overlying nodes in the array (the real array is much larger).
I have managed to identify duplicates using, but unsure of how to proceed:
duplicates = nodes.group_by{|i| [i.x, i.y] }.select{|k,v| v.length > 1}.values
However I don't want a new array, I wish to amend the original "nodes" array attributes.
Thanks

Let's start by creating a class of nodes.
class Nodes
attr_accessor :name, :x, :y
def initialize(name, x, y)
#name = name
#x = x
#y = y
end
end
Next, create the instances shown in the problem.
nodes = [
Nodes.new("node1", 267555, 666777),
Nodes.new("node2", 267555, 666777),
Nodes.new("node3", 245698, 656400),
Nodes.new("node4", 267555, 666777)
]
#=> [#<Nodes:0x00005c7949ee8e40 #name="node1", #x=267555, #y=666777>,
# #<Nodes:0x00005c7949f57c50 #name="node2", #x=267555, #y=666777>,
# #<Nodes:0x00005c7949f57958 #name="node3", #x=245698, #y=656400>,
# #<Nodes:0x00005c7949f577a0 #name="node4", #x=267555, #y=666777>]
Now modify the y values as desired. For this we make use of the form of the method Hash::new that takes an argument called the default value. If a hash h has been created this way, h[k] will return the default value if it does not have a key k. This is sometimes called a counting hash.
nodes.each_with_object(Hash.new(0)) do |inst,h|
y = inst.y
inst.y += 3 * h[y] if h.key?(y)
h[y] += 1
end
#=> {666777=>3, 656400=>1}
Let's see what nodes looks like now.
nodes
#=> [#<Nodes:0x00005c7949ee8e40 #name="node1", #x=267555, #y=666777>,
# #<Nodes:0x00005c7949f57c50 #name="node2", #x=267555, #y=666780>,
# #<Nodes:0x00005c7949f57958 #name="node3", #x=245698, #y=656400>,
# #<Nodes:0x00005c7949f577a0 #name="node4", #x=267555, #y=666783>]
nodes.map { |inst| [inst.name, inst.x, inst.y] }
#=> [["node1", 267555, 666777],
# ["node2", 267555, 666780],
# ["node3", 245698, 656400],
# ["node4", 267555, 666783]]

Related

merge the array of array in ruby on rails

I have one array like below
[["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
And I want result like below
"GJ, MP, KL, HR, MH"
First element of array ["GJ","MP"]
Added is in the answer_string = "GJ, MP"
Now Find MP which is the last element of this array in the other where is should be first element like this ["MP","KL"]
after this I have to add KL in to the answer_string = "GJ, MP, KL"
This is What I want as output
Given
ary = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
(where each element is in fact an edge in a simple graph that you need to traverse) your task can be solved in a quite straightforward way:
acc = ary.first.dup
ary.size.times do
# Find an edge whose "from" value is equal to the latest "to" one
next_edge = ary.find { |a, _| a == acc.last }
acc << next_edge.last if next_edge
end
acc
#=> ["GJ", "MP", "KL", "HR", "MH"]
Bad thing here is its quadratic time (you search through the whole array on each iteration) that would hit you badly if the initial array is large enough. It would be faster to use some auxiliary data structure with the faster lookup (hash, for instance). Smth. like
head, *tail = ary
edges = tail.to_h
tail.reduce(head.dup) { |acc, (k, v)| acc << edges[acc.last] }
#=> ["GJ", "MP", "KL", "HR", "MH"]
(I'm not joining the resulting array into a string but this is kinda straightforward)
d = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
o = [] # List for output
c = d[0][0] # Save the current first object
loop do # Keep looping through until there are no matching pairs
o.push(c) # Push the current first object to the output
n = d.index { |a| a[0] == c } # Get the index of the first matched pair of the current `c`
break if n == nil # If there are no found index, we've essentially gotten to the end of the graph
c = d[n][1] # Update the current first object
end
puts o.join(',') # Join the results
Updated as the question was dramatically changed. Essentially, you navigating a graph.
I use arr.size.times to loop
def check arr
new_arr = arr.first #new_arr = ["GJ","MP"]
arr.delete_at(0) # remove the first of arr. arr = [["HR","MH"],["MP","KL"],["KL","HR"]]
arr.size.times do
find = arr.find {|e| e.first == new_arr.last}
new_arr << find.last if find
end
new_arr.join(',')
end
array = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
p check(array)
#=> "GJ,MP,KL,HR,MH"
Assumptions:
a is an Array or a Hash
a is in the form provided in the Original Post
For each element b in a b[0] is unique
First thing I would do is, if a is an Array, then convert a to Hash for faster easier lookup up (this is not technically necessary but it simplifies implementation and should increase performance)
a = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]]
a.to_h
#=> {"GJ"=>"MP", "HR"=>"MH", "MP"=>"KL", "KL"=>"HR"}
UPDATE
If the path will always be from first to end of the chain and the elements are always a complete chain, then borrowing from #KonstantinStrukov's inspiration: (If you prefer this option then please given him the credit ✔️)
a.to_h.then {|edges| edges.reduce { |acc,_| acc << edges[acc.last] }}.join(",")
#=> "GJ,MP,KL,HR,MH"
Caveat: If there are disconnected elements in the original this result will contain nil (represented as trailing commas). This could be solved with the addition of Array#compact but it will also cause unnecessary traversals for each disconnected element.
ORIGINAL
We can use a recursive method to lookup the path from a given key to the end of the path. Default key is a[0][0]
def navigate(h,from:h.keys.first)
return unless h.key?(from)
[from, *navigate(h,from:h[from]) || h[from]].join(",")
end
Explanation:
navigation(h,from:h.keys.first) - Hash to traverse and the starting point for traversal
return unless h.key?(key) if the Hash does not contain the from key return nil (end of the chain)
[from, *navigate(h,from:h[from]) || h[from]].join(",") - build a Array of from key and the recursive result of looking up the value for that from key if the recursion returns nil then append the last value. Then simply convert the Array to a String joining the elements with a comma.
Usage:
a = [["GJ","MP"],["HR","MH"],["MP","KL"],["KL","HR"]].to_h
navigate(a)
#=> "GJ,MP,KL,HR,MH"
navigate(a,from: "KL")
#=> "KL,HR,MH"
navigate(a,from: "X")
#=> nil

Iterating through a dictionary (hash table) and packing into a struct

I'm writing a program in Python 3, and one task I'd like to do is to take a dictionary and send it over a network (using UDP). But since I can't send a dictionary over UDP without first converting it into raw byte data, I've decided to use the struct library and pack the keys and values into a bytearray.
My desired struct format is this:
I decided to use the struct.pack_into() function, since I want to iterate over each (key, value) in the dictionary and pack them.
Here's my code so far with an example dictionary:
import struct
my_dict = {2: 4, 3: 1} # I have 4 entries in this example.
# The length of my_dict is 2; multiply by 2 to get the desired value of 4.
num_entries = 2 * len(my_dict.keys())
# I need to pack "num_entries" itself, then each (key, value) entry.
# I multiply this total by 2 at the end because I need each value to be
# 2 bytes.
# E.g. if num_entries is 2, then I need space to pack the "2", then
# key_one, value_one, key_two, value_two. That's 10 bytes total.
b = bytearray((1 + num_entries) * 2)
for i, (key, value) in enumerate(my_dict.items(), start=1):
struct.pack_into('!2H', b, i, key, value) # Use 'i' as the offset.
print(b)
# Now prepend num_entries (2, in this case) to the top of the struct.
struct.pack_into('!H', b, 0, num_entries)
# Print the final struct.
print(b)
My desired final struct output is this:
bytearray(b'\x00\x02\x00\x02\x00\x04\x00\x03\x00\x01')
But instead I get this:
struct at this point in the loop: bytearray(b'\x00\x00\x02\x00\x04\x00\x00\x00\x00\x00')
struct at this point in the loop: bytearray(b'\x00\x00\x00\x03\x00\x01\x00\x00\x00\x00')
bytearray(b'\x00\x02\x00\x03\x00\x01\x00\x00\x00\x00') # The final bytearray. key=2, value=4 is not present!
It seems that I'm not packing into the struct at the correct positions. I'd like to get each key and value from my_dict and pack them, then move to the next key and value and pack them in the next two bytes over (hence my use of enumerate() to keep an iterator), and so on. Refer to the picture above. But it appears that the bytes are instead getting overwritten.
What am I doing wrong, and how can I correct it?
I eventually found another solution. Rather than using struct.pack_into() and enumerate(), I just called the extend() method of bytearray(), like so:
import struct
my_dict = {2: 4, 3: 1} # I have 2 entries (keys) in this example.
num_entries = len(my_dict.keys())
b = bytearray()
b.extend(struct.pack('!H', num_entries)) # Prepend the number of entries.
for key, value in my_dict.items():
b.extend(struct.pack('!2H', key, value))
# Print the final bytearray.
print(b)
The above gives me the desired output:
bytearray(b'\x00\x02\x00\x02\x00\x04\x00\x03\x00\x01')

Cost efficient algorithm to group array of sets

Can anyone help me out with some effectively good algorithm to carry out the following task:
I got a file of unique row numbers with an array of integer numbers per row.
I need to check each single row for the values of an array that show up in different rows and put them in one group. Here is an example how it may look:
Row Number; Array of data[...]
L1; [1,2,3,4,5]
L2; [2,3]
L3: [8,9]
L4: [6]
L5; [7]
L6; [5,6]
Based on these input data, I expect the algorithm to produce the result:
Group N; Array of rows [...]
G1; [L1,L2,L4,L6]
G2; [ L3]
G3; [ L5]
P.S the original dataset accounts for hundreds of millions of rows and can contain close to a million of array elements... time efficiency is a concern.
Thanks
I believe this is equivalent to finding connected components of a graph in which:
The vertices correspond to the initial row numbers
There is an edge between two vertices x and y if there is a common element in the array for x and the array for y
This can be done efficiently using a disjoint set data structure as follows:
MakeSet(d) for each of the data values d (1,2,3,4,5,6,7,8,9 in your example)
For each row with array A, call join(A[0],A[i]) for each choice of i.
This will produce a set for each connected component. You can then produce your output array by iterating over the rows a second time:
set output to an array of empty lists
for each row r
A = array for row r
id = find(A[0])
output[id].append(r)
Example Python Code
from collections import defaultdict
data=[[1,2,3,4,5],
[2,3],
[8,9],
[6],
[7],
[5,6]]
N=max(max(A) for A in data)
rank=[0]*(N+1)
parent=range(N+1)
def Find(x):
"""Find representative of connected component"""
if parent[x] != x:
parent[x] = Find(parent[x])
return parent[x]
def Union(x,y):
"""Merge sets containing elements x and y"""
x = Find(x)
y = Find(y)
if x == y:
return
if rank[x]<rank[y]:
parent[x] = y
elif rank[x]>rank[y]:
parent[y] = x
else:
parent[y] = x
rank[x] += 1
# First join all data
for row,A in enumerate(data):
for x in A:
Union(A[0],x)
# Then place rows into sets
D=defaultdict(list)
for row,A in enumerate(data):
D[Find(A[0])].append(row+1)
# Then display output
for i,L in enumerate(D.values()):
print i+1,L
Running this code prints the output:
1 [3]
2 [1, 2, 4, 6]
3 [5]

Create binary tree of fixed size

I am trying to create a binary tree. Only thing I am given is the number of the nodes in the tree. The first thing popped in to my head is to use an index(BFS order) to keep the track of the number of total nodes, then use a recursive definition. Here is my pseudocode to do that.
N = 10 //binary tree total node count
i = 0 //global integer
function()
if i > N
return True
create node i
i = i + 1
function(i) //left
i = i + 1
function(i) //right
I have to use a global variable in this definition which makes me feel like maybe I am violating recursion rules. Is there a better way to do what I do, if this is the way, can it be improved ?
Note: I am asking about the theoretical method, not the code.
Edit: I just realized this method fails. I am open for suggestions.
Clarification: The requirement for this tree is not to add an element to a depth, if the previous depth is not filled with nodes(all nodes have 2 children), pardon me for not mentioning this before, as for the stack I mentioned in the comments, it has nothing to do with the question, just the regular way of traversing trees iteratively.
A tree consists of three elements if defined recursively:
a root node
a left subtree, which is a tree itself
a right subtree, which is a tree itself
all of these may be NULL.
Now we can distribute the numbers in a range [a, b] into a tree in the following manner:
root contains (a + b) / 2
left subtree is built of range [a, (a + b) / 2 - 1] recursively
right subtree is built of the range [(a + b) / 2 + 1, b] recursively
A range with higher start than end may be considered as empty and results in a node being NULL. This distribution ensures that the left and right subtree differ at most by 1 in size and that each level is entirely filled up, before another level is getting filled.
E.g.:
N = 6
[0, 5]
[0, 1] 2 [3, 5]
[0] 1 [] [3] 4 [5]
[] 0 [] [] 3 [] [] 5 []
In addition this algorithm builds a BST (actually this basically the "reverse" of a binary search). Now for the algorithm itself:
function(a, b):
if b < a: return NULL
n = create node (a + b) / 2
n.left = function(a, (a + b) / 2 - 1)
n.right = function((a + b) / 2 + 1, b)
return n
The tree can be generated by calling:
function(1, N)
Alternatively any other parameters a and b should work, where a + N - 1 = b holds. The two parameters represent the range (both inclusive) which should be held by the tree.

array of lists in r

Suppose if I want to have 10 element array each element is a list/map. I am doing this:
x = array(list(), 10)
x[1][[ "a" ]] = 1
Warning message:
In x[1][["a"]] = 1 :
number of items to replace is not a multiple of replacement length
>
Is this the right approach? I want each element of the array to be a map.
What you're calling an "array" is usually just called a list in R. You're getting tripped up by the difference between [ and [[ for lists. See the section "Recursive (list-like) objects" in help("[").
x[[1]][["a"]] <- 1
UPDATE:
Note that the solution above creates a list of named vectors. In other words, something like
x[[1]][["a"]] <- 1
x[[1]][["b"]] <- 1:2
won't work because you can't assign multiple values to one element of a vector. If you want to be able to assign a vector to a name, you can use a list of lists.
x[[1]] <- as.list(x[[1]])
x[[1]][["b"]] <- 1:2
If you really want to do this, then, because the elements of the lists in each element of the array do not have names, you can't index by a character vector. In your example, there is no x[1][[ "a" ]]:
> x[1][[ "a" ]]
NULL
If there are no names then you need to index by a numeric:
> x[1][[ 1 ]] <- 1
[1] 1
It would seem more logical to have a list though than an array:
> y <- vector(mode = "list", length = 10)
> y
[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
[[5]]
NULL
....

Resources