Ruby: reduce duplication while initialize hash - ruby

array = [0, 0.3, 0.4, 0.2, 0.6]
hash = {
"key1" => array[0..2],
"key2" => array[0..3],
"key3" => array,
"key4" => array,
"key5" => array,
"key6" => array,
"key7" => array
}
Is there a way I can remove the duplication by doing something like
hash = {
"key1" => array[0..2],
"key2" => array[0..3],
%(key3, key4, key5, key6, key7).each {|ele| ele => array}
}

Try
array = [0, 0.3, 0.4, 0.2, 0.6]
hash = {
"key1" => array[0..2],
"key2" => array[0..3]
}
%w(key3 key4 key5 key6 key7).each {|ele| hash[ele] = array}

array = [0, 0.3, 0.4, 0.2, 0.6]
h = Hash[*Array.new(7) {|x| ["key#{x+1}", array[0..(x<2?x+2:-1)]]}.flatten(1)]
h # => {"key1" => [0, 0.3, 0.4], "key2" => [0.3, 0.4, 0.2],...}

Here's a couple variations on a theme. They work with 1.8.7 or 1.9.2. The insertion order is maintained with 1.9.2 'cause that's what it does:
require 'pp'
array = [0, 0.3, 0.4, 0.2, 0.6]
hash = ('key3'..'key7').entries.inject({}) { |m, e| m[e] = array; m }
hash.merge!('key1' => array[0..2], 'key2' => array[0..3])
pp hash
puts '-' * 40
hash = {
'key1' => array[0..2],
'key2' => array[0..3]
}.merge(('key3'..'key7').entries.inject({}) { |m, e| m[e] = array; m })
pp hash
puts '-' * 40
# I think this is the most readable/maintainable
hash = {
'key1' => array[0..2],
'key2' => array[0..3]
}
('key3'..'key7').entries.inject(hash) { |m, e| m[e] = array; m }
pp hash
Which output:
# >> {"key3"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key4"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key5"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key6"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key7"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key1"=>[0, 0.3, 0.4],
# >> "key2"=>[0, 0.3, 0.4, 0.2]}
# >> ----------------------------------------
# >> {"key1"=>[0, 0.3, 0.4],
# >> "key2"=>[0, 0.3, 0.4, 0.2],
# >> "key3"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key4"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key5"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key6"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key7"=>[0, 0.3, 0.4, 0.2, 0.6]}
# >> ----------------------------------------
# >> {"key1"=>[0, 0.3, 0.4],
# >> "key2"=>[0, 0.3, 0.4, 0.2],
# >> "key3"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key4"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key5"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key6"=>[0, 0.3, 0.4, 0.2, 0.6],
# >> "key7"=>[0, 0.3, 0.4, 0.2, 0.6]}

Here is another version:
hash = {
"key1" => array[0..2],
"key2" => array[0..3]
}.tap { |h| ("key3".."key7").each{|k| h[k]=array}}

Related

How to represent an array of mixed-types in OpenAPI3?

I have this array of items:
[
1590340491.05,
95,
[1, 0, -0.01, 0.09, 0.98, 0.17, 0.01, 0, 0.98, 0.17, 0.02, 0, 1, 0, 0.04, -0.08, 0.98, 0.17, -0.05, 0, 1, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
["9-1-2-1"]
]
I'm struggling to present this as an example in OpenApi3 in Swagger using YAML.
I've tried:
GraphData:
type: array
items:
anyOf:
- type: number
- type: array
items:
type: number
- type: array
items:
type: string
minItems: 5
example: [ 1590340491.05, 95, [1, 0, -0.01, 0.09, 0.98, 0.17, 0.01, 0, 0.98, 0.17, 0.02, 0, 1, 0, 0.04, -0.08, 0.98, 0.17, -0.05, 0, 1, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0], ["9-1-2-1"] ]
But it ends up displaying this:
[
75963062.21066117,
-63391615.15094785,
-10375297.783109933,
45431809.68164119,
13144786.222638994
]

How do I generate a hash values of 3 arrays in Ruby?

Given I have 3 arrays that are mapped to each other.
fruit = ['apple', 'avocado', 'banana']
color = ['red', 'purple', 'yellow']
price = [30, 20, 50]
How do create an array of hashes with the following value
[
{fruit: 'apple', color: 'red', price: 30},
{fruit: 'avocado', color: 'purple', price: 20},
{fruit: 'banana', color: 'yellow', price: 50}
]
You can use zip to interleave the arrays, and then map them into an array of hashes:
fruit.zip(color, price).map { |f, c, p| { fruit: f, color: c, price: p } }
# => [{:fruit=>"apple", :color=>"red", :price=>30},
# => {:fruit=>"avocado", :color=>"purple", :price=>20},
# => {:fruit=>"banana", :color=>"yellow", :price=>50}
# => ]

Efficiently process all possible 2D array combinations in Perl

I have a 2D array containing numbers. I am attempting to work out the product of multiplying a single number from each sub-array with one from each of the other sub-arrays; I then need to do this for all possible combinations.
The aim is that I input a file of the frequency of individual events, and get an output of the probability of a particular series of these events happening, with one event from each set.
I have fudged together some code with the help of a previous question:
for my $aref ( getCartesian(#freq) ) {
my $p = 1;
foreach my $n (#$aref) {
$p = $p * $n;
}
print "$p\n";
}
sub getCartesian {
my #input = #_;
my #ret = map [$_], #{ shift #input };
for my $a2 (#input) {
#ret = map {
my $v = $_;
map [#$v, $_], #$a2;
}
#ret;
}
return #ret;
}
where #freq is an array of arrays, such as:
#freq = [0.1, 0.2, 0.3,]
[0.4, 0.5, 0.6,]
[0.7, 0.8, 0.9,]; `and ~ 20 more sub arrays`
This works fine for a small test file, but when I give it my required input of 24 sub-arrays with 3 items each, the generation of combinations is clearly far too intensive, with 3^24 possibilities.
I have run it on a machine with 22 GB RAM, and it maxed out after 4 minutes before any output.
My question is, how could I modify the code so that I can print out $p for each combination, without having to hold the whole set of combinations in memory, which kills it. I presume that time would be the only limiting factor for computation then, not resources.
Edit: a method in base Perl without packages would be great. I don't have admin on the HPC facility sadly,
Set::CrossProduct lets you iterate through the Cartesian product so you don't have to store everything in memory:
use List::Util qw(reduce);
use Set::CrossProduct;
my #array = (
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9]
);
my $iterator = Set::CrossProduct->new(\#array);
while (my $tuple = $iterator->get) {
say '(', join(', ', #$tuple), '): ', reduce { $a * $b } #$tuple;
}
Outputs:
(0.1, 0.4, 0.7): 0.028
(0.1, 0.4, 0.8): 0.032
(0.1, 0.4, 0.9): 0.036
(0.1, 0.5, 0.7): 0.035
(0.1, 0.5, 0.8): 0.04
(0.1, 0.5, 0.9): 0.045
(0.1, 0.6, 0.7): 0.042
(0.1, 0.6, 0.8): 0.048
(0.1, 0.6, 0.9): 0.054
(0.2, 0.4, 0.7): 0.056
(0.2, 0.4, 0.8): 0.064
(0.2, 0.4, 0.9): 0.072
(0.2, 0.5, 0.7): 0.07
(0.2, 0.5, 0.8): 0.08
(0.2, 0.5, 0.9): 0.09
(0.2, 0.6, 0.7): 0.084
(0.2, 0.6, 0.8): 0.096
(0.2, 0.6, 0.9): 0.108
(0.3, 0.4, 0.7): 0.084
(0.3, 0.4, 0.8): 0.096
(0.3, 0.4, 0.9): 0.108
(0.3, 0.5, 0.7): 0.105
(0.3, 0.5, 0.8): 0.12
(0.3, 0.5, 0.9): 0.135
(0.3, 0.6, 0.7): 0.126
(0.3, 0.6, 0.8): 0.144
(0.3, 0.6, 0.9): 0.162

Nested Match & Sum In Ruby

Currently I have this array =
[["abc", [0.0, 1.0, 2.0, 3.0], "Testing"], ["efg", [1.0, 2.0, 3.0, 4.0], "Testing"]]
Condition:
if each of nested array index2 is the same then I want to sum up with both
[0.0 + 1.0, 1.0 + 2.0, 2.0 + 3.0, 3.0 + 4.0] = [1.0, 3.0, 5.0, 7.0]
The final result I want:
[["efg", [1.0, 3.0, 5.0, 7.0], "Testing"]]
Is there any way or suggestion to obtain this result?
I've had fun building this in TDD:
def nested_match_sum(data)
grouped = data.group_by(&:last)
grouped.values.map do |array|
array.inject(nil) do |result, elem|
if result
elem[1] = array_position_sum(elem[1], result[1])
end
elem
end
end
end
def array_position_sum(first, second)
first.zip(second).map do |couple|
couple.first + couple.last
end
end
require 'rspec/autorun'
describe "#nested_match_sum" do
let(:data) do
[
["abc", [0.0, 1.0, 2.0, 3.0], "Testing"],
["efg", [1.0, 2.0, 3.0, 4.0], "Testing"]
]
end
it "groups by last element and aggregates the sum" do
expect(nested_match_sum(data)).to eq(
[["efg", [1.0, 3.0, 5.0, 7.0], "Testing"]]
)
end
context "giving multiple keys" do
let(:data) do
[
["abc", [0.0, 1.0, 2.0, 3.0], "Testing"],
["efg", [1.0, 2.0, 3.0, 4.0], "Testing"],
["abc", [0.0, 1.0, 2.0, 3.0], "Another"],
["ghj", [2.0, 3.0, 4.0, 5.0], "Another"]
]
end
it "works aswell" do
expect(nested_match_sum(data)).to eq([
["efg", [1.0, 3.0, 5.0, 7.0], "Testing"],
["ghj", [2.0, 4.0, 6.0, 8.0], "Another"]
])
end
end
end
describe "#array_position_sum" do
let(:first) { [1, 2, 3] }
let(:second) { [4, 5, 6] }
it "sums two arrays by position" do
expect(array_position_sum(first, second)).to eq(
[5, 7, 9]
)
end
end

Why does it matter how I execute this code?

I have built the following simple synth structure which creates a synth and routes its output through an effects unit:
b = Bus.audio(numChannels: 2);
SynthDef(
"mySynth",
{
|freq, amp, gate = 1|
var vol = 0.5;
var audio = Pulse.ar(freq, 0.5);
var env = EnvGen.kr(Env.perc, doneAction:2);
audio = Pan2.ar(audio, MouseX.kr(-1, 1));
Out.ar(b, audio * env);
}
).add;
SynthDef(
"effects",
{
var audio = In.ar(b, 2);
audio = LPF.ar(audio, MouseY.kr(200, 1000));
//TODO: Implement some crazy, revolutionary effects
Out.ar(0, audio);
}
).add;
// **** Dividing line for executing the code ****
e = Synth(\effects);
p = Pbind(*[
instrument: \mySynth,
scale: #[0, 2, 4, 5, 7, 9, 11],
degree: Pseq([3, 3, 9, 9, 2, 9, 9, 3, 5, 7], inf),
dur: Pseq([0.2, 0.2, 0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.1, 0.1], inf),
amp: Pseq([1, 0.6, 0.9, 0.3, 0.4, 0.9, 0.6, 0.85, 0.3, 0.4], inf),
]);
p.play;
This only produces audible output when I execute the code in a particular way:
I can execute each block individually, in order, and I get audible output.
I can execute the first blocks up to the 'dividing line' comment, then the following blocks, and I get audible output.
If I execute all the code together, I don't get audible output.
I'm guessing there has to be some delay between declaring a SynthDef and then instantiate it using Synth(), while the server does set setup stuff. Can anyone shed any light?
I usually get around this with the Server.sync() method. It pauses execution of the enclosing thread (e.g. a Routine) until all asynchronous server commands have been completed. This includes sending SynthDefs and allocating Buffers. You can pass a Condition argument to Server.sync() for more explicit control.
so for example, you can execute this block in one go:
s = Server.local;
s.boot;
s.doWhenBooted({
Routine {
SynthDef.new(\sine, {
arg out=0, hz=220, dur=4.0;
var snd, amp;
snd = SinOsc.ar(hz);
amp = EnvGen.ar(Env.linen(0.1, dur, 0.1), doneAction:2);
Out.ar(out, (amp*snd).dup);
}).send(s);
s.sync; // waits here
x = Synth.new(\sine);
}.play;
});
It is because you can't just "add" SynthDefs to the server and create an instance of said synth in the same execution. If you "play" the synths as they are executed then an instance of them gets added to the server so that when you call the Synth up for execution it will already be loaded. Working code is included below.
(
b = Bus.audio(numChannels: 2);
SynthDef(
"mySynth",
{
|freq, amp, gate = 1|
var vol = 0.5;
var audio = Pulse.ar(freq, 0.5);
var env = EnvGen.kr(Env.perc, doneAction:2);
audio = Pan2.ar(audio, MouseX.kr(-1, 1));
Out.ar(b, audio * env);
}
).play;
SynthDef(
"effects",
{
var audio = In.ar(b, 2);
audio = LPF.ar(audio, MouseY.kr(200, 1000));
//TODO: Implement some crazy, revolutionary effects
Out.ar(0, audio);
}
).play;
// **** Dividing line for executing the code ****
e = Synth(\effects);
p = Pbind(*[
instrument: \mySynth,
scale: #[0, 2, 4, 5, 7, 9, 11],
degree: Pseq([3, 3, 9, 9, 2, 9, 9, 3, 5, 7], inf),
dur: Pseq([0.2, 0.2, 0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.1, 0.1], inf),
amp: Pseq([1, 0.6, 0.9, 0.3, 0.4, 0.9, 0.6, 0.85, 0.3, 0.4], inf),
]);
p.play;
)
I'm sure you're right that it's to do with the delay between declaring the synthdef and it being ready.
I'm not really experienced enough with sclang to immediately tell you exactly how you should change your code (I generally use scsynth via OSC, only using sclang to write SynthDefs), but you should be able to do something with the optional completionMsg argument to SynthDef.add.

Resources