Using .sort_by to sort a nested array - ruby

I have a nested array that I want to sort by a specific object, some advice would be very much appreciated.
In this example I'd like the output to return sorted by the dates that are nested.
arr = [
[
{
"log"=>[
[
"2016-09-03T00:00:00-03:00",
],
[
"2016-09-01T00:00:00-03:00",
],
[
"2016-09-02T00:00:00-03:00",
]
]
}
]
]

arr = [
[
{
"log"=>[
["2016-09-03T00:00:00-03:00"],
["2016-09-01T00:00:00-03:00"],
["2016-09-02T00:00:00-03:00"]
]
}
]
]
To return a sorted array and not mutate arr:
[[{ "log"=>arr[0][0]["log"].sort_by(&:first) }]]
#=> [[{"log"=>[
# ["2016-09-01T00:00:00-03:00"],
# ["2016-09-02T00:00:00-03:00"],
# ["2016-09-03T00:00:00-03:00"]
# ]}]]
To sort in place:
arr[0][0]["log"] = arr[0][0]["log"].sort_by(&:first)
#=> [["2016-09-01T00:00:00-03:00"],
# ["2016-09-02T00:00:00-03:00"],
# ["2016-09-03T00:00:00-03:00"]]
arr
#=> [[{"log"=>[
# ["2016-09-01T00:00:00-03:00"],
# ["2016-09-02T00:00:00-03:00"],
# ["2016-09-03T00:00:00-03:00"]
# ]}]]

Related

Transform array of nested Hashes into flat array of non-nested hashes

I want to transform the given array into result array:
given = [{
"foo_v1_4" => [{
"derivate_version" => 0,
"layers" => {
"tlayer" => {
"baz" => {
"three" => 0.65
},
"bazbar" => {
"three" => 0.65
}
}
}
}]
}]
# the value of key :one is first hash key (foo_v1_4) plus underscore (_) plus derivate_version (0)
result = [{
one: 'foo_v1_4_0',
tlayer: 'baz',
three: '0.6'
},
{
one: 'foo_v1_4_0',
tlayer: 'bazbar',
three: '0.6'
}
]
What I tried:
given.each do |el |
el.each do |derivat |
derivat.each do |d |
d.each do |layer |
layer.each do |l |
derivat = "#{d}_#{l['derivate_version']}"
puts derivat
end
end
end
end
end
I'm struggling at iterating through "layers" hash, the amount of elements in layers is equal to the amount of elements in result array.
It helps to format the objects so we can better see their structures:
given = [
{
"foo_v1_4" => [
{ "derivate_version" => 0,
"layers" => {
"tlayer" => {
"baz" => { "three" => 0.65 },
"bazbar" => { "three" => 0.65 }
}
}
}
]
}
]
result = [
{
one: 'foo_v1_4_0',
tlayer: 'baz',
three: '0.6'
},
{
one: 'foo_v1_4_0',
tlayer: 'bazbar',
three: '0.6'
}
]
We can begin by writing the structure of result:
result = [
{
one:
tlayer:
three:
},
{
one:
tlayer:
three:
}
]
We see that
given = [ { "foo_v1_4" => <array> } ]
The values of the keys :one in the hash result[0] is therefore the first key of the first element of given:
one_val = given[0].keys[0]
#=> "foo_v1_4"
result = [
{
one: one_val
tlayer:
three:
},
{
one: one_val
tlayer:
three:
}
]
All the remaining objects of interest are contained in the hash
h = given[0]["foo_v1_4"][0]["layers"]["layer"]
#=> {
# "baz"=>{ "three"=>0.65 },
# "bazbar"=>{ "three"=>0.65 }
# }
so it is convenient to define it. We see that:
h.keys[0]
#=> "baz"
h.keys[1]
#=> "bazaar"
h["bazbar"]["three"]
#=> 0.65
Note that it generally is not good practice to assume that hash keys are ordered in a particular way.
We may now complete the construction of result,
v = h["bazbar"]["three"].truncate(1)
#=> 0.6
result = [
{
one: one_val,
tlayer: h.keys[0],
three: v
},
{ one: one_val,
tlayer: h.keys[1],
three: v
}
]
#=> [
# { :one=>"foo_v1_4", :tlayer=>"baz", :three=>0.6 },
# { :one=>"foo_v1_4", :tlayer=>"bazbar", :three=>0.6 }
# ]
The creation of the temporary objects one_val, h, and v improves time- and space-efficiency, makes the calculations easier to test and improves the readability of the code.
Try the below:
result = []
given.each do |level1|
level1.each do |key, derivate_versions|
derivate_versions.each do |layers|
# iterate over the elements under tlayer
layers.dig('layers', 'tlayer').each do |tlayer_key, tlayer_value|
sub_result = {}
# key - foo_v1_4, layers['derivate_version'] - 0 => 'foo_v1_4_0'
sub_result[:one] = key + '_' + layers['derivate_version'].to_s
# talyer_key - baz, barbaz
sub_result[:tlayer] = tlayer_key
# talyer_value - { "three" => 0.65 }
sub_result[:three] = tlayer_value['three']
result << sub_result
end
end
end
end
The value of result will be:
2.6.3 :084 > p result
[{:one=>"foo_v1_4_0", :tlayer=>"baz", :three=>0.65}, {:one=>"foo_v1_4_0", :tlayer=>"bazbar", :three=>0.65}]

Merge duplicated values in array of hashes

I have an array of hashes like this
arr_of_hashes = [
{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
]
I want to remove duplicates. Also, among:
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
I want to keep only the last line. How can I do that?
I tried to do:
sorted_arr = arr_of_hashes.uniq
arr_of_hashes.reverse.uniq { |hash| hash["started_at"] }.reverse
About pass block to uniq and about reverse.
#result
[
{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
]
Something like this?
[2] pry(main)> arr_of_hashes.reject { |h| h['started_at'] == h['stopped_at'] }
[
[0] {
"started_at" => "2018-07-16",
"stopped_at" => "still active"
}
]
Its not clear form your question what output you want to get
arr_of_hashes.each_with_object({}) { |g,h| h.update(g["started_at"]=>g) }.values
#=> [{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
# {"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
# {"started_at"=>"2018-07-16", "stopped_at"=>"still active"}]
See Hash#update (a.k.a. merge!) and note that values's receiver is as follows.
arr_of_hashes.each_with_object({}) { |g,h| h.update(g["started_at"]=>g) }
#=> {"2018-07-11"=>{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
# "2018-07-13"=>{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
# "2018-07-16"=>{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}}

How do I select an anonymous hash within an array?

I have the following array where I want to select each of the keys:
names = [
{"Ánias"=>{:gender=>"male", :nationality=>"faroese"}},
{"Annfinnur"=>{:gender=>"male", :nationality=>"faroese"}},
{"Ansgar"=>{:gender=>"male", :nationality=>"faroese"}}
]
How would I go about selecting all the names ("Ánias", "Annfinnur", "Ansgar")?
Just do
names = [
{"Ánias"=>{:gender=>"male", :nationality=>"faroese"}},
{"Annfinnur"=>{:gender=>"male", :nationality=>"faroese"}},
{"Ansgar"=>{:gender=>"male", :nationality=>"faroese"}}
]
names.map { |h| h.keys.first }
# => ["Ánias", "Annfinnur", "Ansgar"]

Comparing Deep Nested Array of Array of Hashes in Ruby

I have an array of array of hashes which can be nested any level deep.
array = [
[ ['a','2'], ['b','5'] ],
[ ['c','4'], ['d','5'] ],
[ ['e','6'], [f,7] ],
...]
In the first stage I need to compare each consecutive hash - keep one of the elements and discarding the other.
In the second step the selected element of hash 1 have to be compared to selected element of the hash 2. This process has to continue till i am left with just one hashed element.
How do i do this i Ruby ?
thanks a lot for answering
You can do this with ==:
array1 = [
[ ['a','2'], ['b','5'] ],
[ ['c','4'], ['d','5'] ],
[ ['e','6'], ['f',7] ]
]
array2 = [
[ ['a','2'], ['b','5'] ],
[ ['c','4'], ['d','5'] ],
[ ['e','6'], ['f',7] ]
]
array3 = [
[ ['not','equal'] ]
]
array1 == array2
# => true
array2 == array3
# => false
See Array#== for specifics.

Include one array inside another complex array using ruby

I have the following arrays:
array = [ [link_text1, link1],[link_text2, link2], ... ]
array = [ [views1],[views2], ... ]
How can I can combine them, so I get this array:
[ [link_text1, link1, views1], [link_text2, link2, views2], ... ]
The same as robinst, but a little shorter
a1 = [ ["link_text1", "link1"],["link_text2", "link2"] ]
a2 = [ ["views1"],["views2"] ]
a1.zip(a2).map(&:flatten)
Try a combination of zip and flatten:
a1 = [ ["link_text1", "link1"],["link_text2", "link2"] ]
a2 = [ ["views1"],["views2"] ]
zipped = a1.zip(a2)
array_final = zipped.collect { |a| a.flatten }
#=> [["link_text1", "link1", "views1"], ["link_text2", "link2", "views2"]]

Resources