Related
I have a hash of the following form:
{ 1 => [], 2 => ["A", "B"], 3 => ["C"], 4 => ["B", "C"], 5 => ["D"] }
what's the best way to transform this in Ruby to:
{ "A" => [2], "B" => [2, 4], "C" => [3, 4], "D" => [5], "default" => [1] }
The best way I know.
hash = { 1 => [], 2 => ['A','B'], 3 => ['C'], 4 => ['B','C'], 5 => ['D'] }
new_hash = hash.inject({}) do |result, (key, values)|
values.each do |value|
result[value] ||= []
result[value].push(key)
end
result[:default] = [key] if values.empty?
result
end
puts new_hash
A bit of more functional approach:
array
.flat_map {|k,v| v.product([k])}
.group_by(&:first)
.transform_values {|v| v.map(&:last) }
input = { 1 => [], 2 => ['A', 'B'], 3 => ['C'], 4 => ['B', 'C'], 5 => ['D'], 6 => [] }
output =
input.each_with_object(Hash.new([])) do |(key, values), hash|
values.each { |value| hash[value] += [key] }
hash['default'] += [key] if values.empty?
end
output
# => {"default"=>[1, 6], "A"=>[2], "B"=>[2, 4], "C"=>[3, 4], "D"=>[5]}
I have an array that looks something like this
array = [{ a: 123, b: 'foo', c: 'bar' }, { a: 456, b: 'baz', c: 'qux' }]
I would like to convert this to a hash whose keys are the values of :a in the hashes in array, and whose values consist of hashes with :b and :c.
{ 123 => { b: 'foo', c: 'bar' }, 456 => { b: 'baz', c: 'qux' } }
Is this doable using ruby?
array.each_with_object({}){|e, h| e = e.dup; h[e.delete(:a)] = e}
# => {123=>{:b=>"foo", :c=>"bar"}, 456=>{:b=>"baz", :c=>"qux"}}
If you don't care about side effects:
array.each_with_object({}){|e, h| h[e.delete(:a)] = e}
# => {123=>{:b=>"foo", :c=>"bar"}, 456=>{:b=>"baz", :c=>"qux"}}
I'm not sure what problem you're trying to solve. This is what I come out with:
def group_by_key(array, key)
array.map { |x| [x.delete(key), x] }.to_h
end
It works good on your example:
array = [{ a: 123, b: 'foo', c: 'bar' }, { a: 456, b: 'baz', c: 'qux' }]
group_by_key(array, :a)
#=> {123=>{:b=>"foo", :c=>"bar"}, 456=>{:b=>"baz", :c=>"qux"}}
Assuming that all elements of array (hashes) have a key :a and that array cannot be mutated, use Hash#reject:
array.each_with_object({}) { |g,h| h[g[:a]] = g.reject { |k,_| k == :a } }
#=> {123=>{:b=>"foo", :c=>"bar"}, 456=>{:b=>"baz", :c=>"qux"}}
Using Hash#select method.
array.map { |v| [v.fetch(:a), v.select { |k,_| k != :a }] }.to_h
Exact :a, :b & :c
array.map { |v| [v.fetch(:a), v.select { |k,_| [:b, :c].include? k }] }.to_h
Output
{123=>{:b=>"foo", :c=>"bar"}, 456=>{:b=>"baz", :c=>"qux"}}
Does Perl 6 have a built-in tool to make a deep copy of a nested data structure?
Added example:
my %hash_A = (
a => {
aa => [ 1, 2, 3, 4, 5 ],
bb => { aaa => 1, bbb => 2 },
},
);
my %hash_B = %hash_A;
#my %hash_B = %hash_A.clone; # same result
%hash_B<a><aa>[2] = 735;
say %hash_A<a><aa>[2]; # says "735" but would like get "3"
my %A = (
a => {
aa => [ 1, 2, 3, 4, 5 ],
bb => { aaa => 1, bbb => 2 },
},
);
my %B = %A.deepmap(-> $c is copy {$c}); # make sure we get a new container instead of cloning the value
dd %A;
dd %B;
%B<a><aa>[2] = 735;
dd %A;
dd %B;
Use .clone and .deepmap to request a copy/deep-copy of a data-structure. But don't bet on it. Any object can define its own .clone method and do whatever it wants with it. If you must mutate and therefore must clone, make sure you test your program with large datasets. Bad algorithms can render a program virtually useless in production use.
The dirty way:
#!/usr/local/bin/perl6
use v6;
use MONKEY-SEE-NO-EVAL;
my %hash_A = (
a => {
aa => [ 1, 2, 3, 4, 5 ],
bb => { aaa => 1, bbb => 2 },
},
);
my %hash_B;
EVAL '%hash_B = (' ~ %hash_A.perl ~ ' )';
%hash_B<a><aa>[2] = 735;
say %hash_A;
say %hash_B;
which gives you:
$ perl6 test.p6
{a => {aa => [1 2 3 4 5], bb => {aaa => 1, bbb => 2}}}
{a => {aa => [1 2 735 4 5], bb => {aaa => 1, bbb => 2}}}
If you eval input from external source, always remember to check it first. Anyway, using EVAL is dangerous and should be avoided.
I need a method that will take a hash and return a hash whose keys are from the old hash and values are the size of the arrays in the old hash. I.e.,
{ 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
# return
{ 1 => 3, 2 => 3, 7 => 2 }
Is there any way to implement this?
One way:
h = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
h.merge(h) { |*_,a| a.size }
#=> { 1 => 3, 2 => 3, 7 => 2 }
You can use map to build a new array of [key, value] pairs and then convert it back to a hash using to_h:
input = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
input.map { |key, value| [key, value.length] }.to_h
# => {1=>3, 2=>3, 7=>2}
This is quite straightforward: you can use inject to process all the items one by one and compose the result.
input = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
input.inject({}) do |result, (key, value)|
result.merge(key => value.size)
end
# => {1=>3, 2=>3, 7=>2}
Even without inject, just use .each to loop all the items and construct the result using a temporary support Hash.
Just for the sake of completeness, a solution using each_with_object:
input = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
input.each_with_object({}) { |(k, vs), h| h[k] = vs.size }
#=> {1=>3, 2=>3, 7=>2}
One way is to insert the expected keys and values into the new Hash:
h = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
h2 = {}
h.each {|k, v| h2[k] = v.length}
h2
# => {1=>3, 2=>3, 7=>2}
h.keys.zip(h.values.map &:size).to_h
Or you can try this,
h = { 1 => [1,1,1], 2 => [3,4,5], 7 => [9,12] }
Hash[h.map {|key, value| [key, value.length]}]
# => {1=>3, 2=>3, 7=>2}
How can I delete some elements from an array and select them?
For example:
class Foo
def initialize
#a = [1,2,3,4,5,6,7,8,9]
end
def get_a
return #a
end
end
foo = Foo.new
b = foo.get_a.sth{ |e| e < 4 }
p b # => [1,2,3]
p foo.get_a # => [4,5,6,7,8,9,10]
What I can use instead of foo.get_a.sth?
If you don't need to retain the object id of a:
a = [1,2,3,4,5,6,7,8,9,10]
b, a = a.partition{|e| e < 4}
b # => [1, 2, 3]
a # => [4, 5, 6, 7, 8, 9, 10]
If you do need to retain the object id of a, then use a temporal array c:
a = [1,2,3,4,5,6,7,8,9,10]
b, c = a.partition{|e| e < 4}
a.replace(c)
Rails 6 now has this:
a = [1, 2, 3]
#=> [1, 2, 3]
a.extract! { |n| n.even? }
#=> [2]
a
#=> [1, 3]
If you were only deleting one item, this doesn't require duplicating the array, etc:
array = [{ id: 1 }, { id: 2 }, {id: 3 }]
array.delete_at(array.find_index { |element| element[:id] == 1 })
#=> {:id=>1}
a = [1, 2, 3, 4]
a.dup - (a.delete_if(&:even?))
#=> [2, 4]
a
#=> [1, 3]
a = [1, 2, 3, 4]
b = a.dup - (a.delete_if { |e| e < 4 })
a
#=> [4]
b
#=> [1, 2, 3]
Edit: It sounds like you are just after #select...
a = [1, 2, 3, 4]
a.select { |e| e < 3 }
#=> [1, 2]
I still don't believe ruby doesn't have something for this in its default libraries. There should be #drop method equivalent for this. Surely there's a gem available that would add the functionality to the array class. But who needs gems when you can just break out your own scripts:
#in initializer somewhere
class Array
def exclude(obj)
x = self
x.delete(obj)
x
end
end
I may submit a pull request to the ruby github project for this method. This superman-patch works very well so far.