Sort array using values from separate hash - ruby

I have an array of IDs and a separate hash that dictates how those IDs should be ordered. Something like this:
Array:
[
7,
3,
2,
]
Sort ranks:
{
1 => 50,
2 => 70,
3 => 10,
4 => 80,
5 => 60,
6 => 20,
7 => 0,
}
I want to elegantly reverse sort the first array by using the values of the hash. So the result should be this:
[
2,
3,
7,
]
I know there are brute force methods, but is there an easier and more performant way to do this?

assuming you have order_hash and object_array defined, you can sort using following code:
object_array.sort{ |a, b| order_hash[b[:id]] <=> order_hash[a[:id]] }
you can use some constant for instead of order_hash if you want.

I'm not sure about more performant but here's a relatively simple way
arr = [
{
:id => 7,
},
{
:id => 3,
},
{
:id => 2,
}
]
ranks = {
1 => 50,
2 => 70,
3 => 10,
4 => 80,
5 => 60,
6 => 20,
7 => 0,
}
arr2 = arr.sort_by{|elem| -ranks[elem[:id]]}
arr2 # => [{:id=>2}, {:id=>3}, {:id=>7}]

Use Enumerable#sort_by:
input = [
7,
3,
2,
]
ranks = {
1 => 50,
2 => 70,
3 => 10,
4 => 80,
5 => 60,
6 => 20,
7 => 0,
}
result = input.sort_by{|x| ranks[x]} #=> [7, 3, 2]

Related

How to pick arrays from many arrays so that all elements are rising in a linear way

I have a map with these arrays. What I want from here it choose the fewest item to get from 0-10. For example.
{
'11100171' => [ 0, 1, 2, 3 ],
'11100060' => [ 1, 2, 3 ],
'12100062' => [ 4 ],
'12100012' => [ 5, 6, 7 ],
'12100011' => [ 6, 7 ],
'12100020' => [ 7 ],
'12100121' => [ 7 ],
'11100130' => [ 8, 9 ],
'11100140' => [ 8, 9 ],
'11100470' => [ 8, 9 ],
'11400080' => [ 8, 9, 10 ]
}
I would choose
'11100171' => [ 0, 1, 2, 3 ],
'12100062' => [ 4 ],
'12100012' => [ 5, 6, 7 ],
'11400080' => [ 8, 9, 10 ]
From here.
This is a graph path finding problem. If we assume all arrays consist of consecutive numbers, we can interpret one such array as an edge in a graph that links the node identified by the first number with the node identified by that last number plus 1.
The goal is then to find the shortest path from start to end+1.
For that we can use a breadth-first search in that graph.
Here is an implementation of that idea in JavaScript:
function createAdjacencyList(data) {
let adj = {};
for (let key in data) {
let arr = data[key];
let start = arr[0];
if (!adj[start]) adj[start] = [];
adj[start].push(arr);
}
return adj;
}
function shortestPath(adj, start, end) { // BFS algo
let frontier = [start];
let comeFrom = {};
while (frontier.length) {
let nextFrontier = [];
for (let node of frontier) {
if (node === end + 1) { // Unwind path
let path = [];
do {
arr = comeFrom[node];
path.push(arr);
node = arr[0];
} while (node != start);
return path.reverse();
}
if (adj[node]) {
for (let arr of adj[node]) {
let neighbor = arr.at(-1) + 1;
if (!comeFrom[neighbor]) {
comeFrom[neighbor] = arr;
nextFrontier.push(neighbor);
}
}
}
}
frontier = nextFrontier;
}
}
// Example run
let data = {
'11100171': [0, 1, 2, 3],
'11100060': [1, 2, 3],
'12100062': [4],
'aaaaaaaa': [4, 5],
'12100012': [5, 6, 7],
'12100011': [6, 7],
'12100020': [7],
'12100121': [7],
'11100130': [8, 9],
'11100140': [8, 9],
'11100470': [8, 9],
'11400080': [8, 9, 10],
'xxxxxxxx': [9, 10]
};
let adj = createAdjacencyList(data);
let path = shortestPath(adj, 0, 10);
for (let arr of path) {
console.log(...arr);
}

Sort hash by key in descending order

I have the following hash:
{"2013-08-12"=> 10, "2013-08-13"=> 20, "2013-11-11"=>30, "2013-11-14"=> 40}
What I want to do is to sort it by key (dates in format yyyy-mm-dd) in descending order:
{"2013-11-14"=> 40, "2013-11-11"=>30, "2013-08-13"=> 20, "2013-08-12"=> 10}
Is this possible?
It is possible.
Hash[
{"2013-08-12"=> 10, "2013-08-13"=> 20, "2013-11-11"=>30, "2013-11-14"=> 40}
.sort_by{|k, _| k}.reverse
]
# => {
"2013-11-14" => 40,
"2013-11-11" => 30,
"2013-08-13" => 20,
"2013-08-12" => 10
}

Collect values from an array of hashes

I have a data structure in the following format:
data_hash = [
{ price: 1, count: 3 },
{ price: 2, count: 3 },
{ price: 3, count: 3 }
]
Is there an efficient way to get the values of :price as an array like [1,2,3]?
First, if you are using ruby < 1.9:
array = [
{:price => 1, :count => 3},
{:price => 2, :count => 3},
{:price => 3, :count => 3}
]
Then to get what you need:
array.map{|x| x[:price]}
There is a closed question that redirects here asking about handing map a Symbol to derive a key. This can be done using an Enumerable as a middle-man:
array = [
{:price => 1, :count => 3},
{:price => 2, :count => 3},
{:price => 3, :count => 3}
]
array.each.with_object(:price).map(&:[])
#=> [1, 2, 3]
Beyond being slightly more verbose and more difficult to understand, it also slower.
Benchmark.bm do |b|
b.report { 10000.times { array.map{|x| x[:price] } } }
b.report { 10000.times { array.each.with_object(:price).map(&:[]) } }
end
# user system total real
# 0.004816 0.000005 0.004821 ( 0.004816)
# 0.015723 0.000606 0.016329 ( 0.016334)

Subtract values in hash from corresponding values in another hash

I'd like to be able to subtract two hashes and get a third hash in Ruby.
The two hashes look like this:
h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0
I'd like to be able to call a method on h1 like this:
h1.difference(h2)
and get this hash as a result:
{"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
I'd like to create a new hash with keys from both Hashes and the values of the new hash to be the value of the key in the first hash minus the value of that key in the second hash. The catch is that I'd like this Hash method to work regardless of the case of the keys. In other words, I'd like "Cat" to match up with "cat".
Here's what I have so far:
class Hash
def difference(another_hash)
(keys + another_hash.keys).map { |key| key.strip }.uniq.inject(Hash.new(0)) { |acc, key| acc[key] = (self[key] - another_hash[key]); acc }.delete_if { |key, value| value == 0 }
end
end
This is OK, but, unfortunately, the result isn't what I want.
Any help would be appreciated.
How about converting the hashes to sets.
require 'set'
h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0
p (h1.to_set - h2.to_set)
#=> #<Set: {["Cat", 100], ["Dog", 5], ["Bird", 2]}>
As a recommendation...
I've used something like this in the past:
class Hash
def downcase_keys
Hash[map{ |k,v| [k.downcase, v]}]
end
def difference(other)
Hash[self.to_a - other.to_a]
end
alias :- :difference
end
which lets me do things like:
irb(main):206:0> h1.downcase_keys - h2.downcase_keys
{
"cat" => 100,
"dog" => 5,
"bird" => 2
}
irb(main):207:0> h2.downcase_keys - h1.downcase_keys
{
"cat" => 50,
"dog" => 3,
"bird" => 4,
"mouse" => 75
}
The alias gives you the nice syntax of using - instead of difference, similar to using - for Arrays and Sets. You can still use difference though:
irb(main):210:0> h1.downcase_keys.difference(h2.downcase_keys)
{
"cat" => 100,
"dog" => 5,
"bird" => 2
}
irb(main):211:0> h2.downcase_keys.difference(h1.downcase_keys)
{
"cat" => 50,
"dog" => 3,
"bird" => 4,
"mouse" => 75
}
I always normalize my hash keys, and don't allow variants to leak in. It makes processing the hashes much too difficult when you don't know what the keys are called, so I'd highly recommend doing that as a first step. It's a code-maintenance issue.
Otherwise, you could create a map of the original key names and their normalized names, but you run into problems if your hash contains two unique-case keys, such as 'key' and 'KEY', because normalizing will stomp on one.
Sorry that due to the time limit (I have to take care of my baby boy now), only figured out this stupid but working code:
h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0
h3 = {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
class Hash
def difference(subtrahend)
diff = {}
self.each_pair do |k1, v1|
flag = false
subtrahend.each_pair do |k2, v2|
if k1.downcase == k2.downcase
flag = true
v_diff = v1 - v2
break if v_diff == 0
v_diff > 0 ? diff[k1] = v_diff : diff[k2] = v_diff
end
end
diff[k1] = v1 unless flag
end
subtrahend.each_pair do |k2, v2|
flag = false
self.each_pair do |k1, v1|
if k1.downcase == k2.downcase
flag = true
break
end
end
diff[k2] = -v2 unless flag
end
return diff
end
end
h1.difference(h2) == h3 ? puts("Pass") : puts("Fail") #=> "Pass"
I got this to the resque https://github.com/junegunn/insensitive_hash
then follow your procedure but slighly tweaked as requirement
require 'insensitive_hash'
h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}.insensitive
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}.insensitive
h2.default = 0
class Hash
def difference(another_hash)
(keys + another_hash.keys).map { |key|
key.downcase }.uniq.inject(Hash.new(0)) do |acc, key|
val = self[key] - another_hash[key]
acc[key] = val if val!= 0
acc
end
end
end
h1.difference(h2)
# => {"cat"=>50, "dog"=>2, "bird"=>-2, "mouse"=>-75}
This time I would like to provide another solution: normalized -> store original key value pairs -> grab the original key who has larger value as the key for the difference.
h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0
h3 = {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
class Hash
def difference(subtrahend)
# create a hash which contains all normalized keys
all_pairs = (self.keys.map{|x| x.downcase} + subtrahend.keys.map{|x| x.downcase}).uniq.inject({}) do |pairs, key|
pairs[key] = []
pairs
end
#=> {"mouse"=>[], "cat"=>[], "snake"=>[], "bird"=>[], "dog"=>[]}
# push original key value pairs into array which is the value of just created hash
[self, subtrahend].each_with_index do |hsh, idx|
hsh.each_pair { |k, v| all_pairs[k.downcase].push([k, v]) }
all_pairs.each_value { |v| v.push([nil, 0]) if v.size == idx }
end
#=> {"mouse"=>[[nil, 0], ["Mouse", 75]], "cat"=>[["Cat", 100], ["cat", 50]], "snake"=>[["Snake", 10], ["Snake", 10]], "bird"=>[["Bird", 2], ["BIRD", 4]], "dog"=>[["Dog", 5], ["dog", 3]]}
results = {}
all_pairs.each_value do |values|
diff = values[0][1] - values[1][1]
# always take the key whose value is larger
if diff > 0
results[values[0][0]] = diff
elsif diff < 0
results[values[1][0]] = diff
end
end
return results
end
end
puts h1.difference(h2).inspect #=> {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
h1.difference(h2) == h3 ? puts("Pass") : puts("Fail") #=> "Pass"
According to what you described, this one does a pretty good job. The result is exactly what you've shown (key is not normalized in the final result, but depends on whose value is bigger).

ruby - group by repeating key of multiple hashes

I have the following array
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
how can I get this as result?
{nil => [1,9,0],10 => [2,2,4], 16 => [4,0,0], 5 => [10,2,9], 17=>[0,3,1]}
I've seen that I can use something like this
t.group_by{|h| h['key']}
but I'm not sure if I can put a regexp inside the brackets
Thanks in advance
Javier
EDIT:
Is just want to group by each key of each hash inside the array, if the key is not present then the value is 0 for that hash
How about this one for illegibility:
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
# Create hash of possible keys
keys = t.reduce({}) { |m, h| h.each_key { |k| m[k] = [] }; m }
# Iterate through array, for each hash, for each key, append the
# value if key is in hash or zero otherwise
t.reduce(keys) { |m, h| m.each_key { |k| m[k] << (h[k] || 0) }; m }
puts keys
#=> {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}
Not the most elegant code I've ever written, but it does the job and is easy to understand:
def jqq(a)
keys = []
result = {}
a.each do |h|
keys += h.keys
end
keys.uniq.each do |key|
result[key] = []
a.each do |h|
h.default = 0
result[key] << h[key]
end
end
result
end
t = [
{nil => 1, 10 => 2, 16 => 4, 5=> 10},
{nil => 9, 5 => 2, 17 => 3, 10 => 2},
{10 => 4, 5 => 9, 17 => 1}
]
puts jqq(t)
# {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}
I do not think there is any any function available
Just gave a try with hash
def do_my_work(data)
hash = {}
#get all keys first
arr.map{|m| m.keys}.flatten.uniq.each {|a| hash[a]=[]}
# Now iterate and fill the values
arr.each do |elm|
hash.each do |k,v|
hash[k] << (elm[k].nil? ? 0 : elm[k])
end
end
end
hash = do_my_work(t)
puts hash
# => {nil=>[1, 9, 0], 10=>[2, 2, 4], 16=>[4, 0, 0], 5=>[10, 2, 9], 17=>[0, 3, 1]}

Resources