I have multiple arrays with unknown element count like
a = []
a << [:a, :c, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [:d]
result should be something like ~ (I don't really care details due rounding etc)
r = [:b, :a, :f, :g, :d, :c, :h, :i, :e, :j]
This is how I think it could be done
First we need to expand/distribute equally elements in each array to same length, so we get something like
a << [nil, :a, nil, :c, nil, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [nil, nil, :d, nil, nil]
Next we interleave them as typically would do
r = a.shift
a.each { |e| r = r.zip(e) }
r = r.flatten.compact
My current problem is how to equally (as much as it's possible) distribute those elements across array? There could be one array with 4 elements and other with 5, but probably biggest should go first.
Of course would be nice to see if there's any other way to achieve this :)
I would use a sort to do this, based on element index postion, divided by size of array, plus some offset based on array id, to keep things consistent (if you don't need consistency, you could use a small random offset instead).
a = [:a,:b]
b = [:c]
c = [:d,:e,:f]
d = [:g:,:h,:i,:j]
def sort_pos array, id
(1..array.size).map { |i| (i - 0.5 + id/1000.0)/(array.size + 1e-6) }
end
# Combine all the arrays with their sort index, assigning ids to each array for consistency.
# Depending on how you receive these arrays, this structure can be built up programatically,
# as long as you add an array plus its sort index numbers at the same time
combined = (a + b + c + d).zip( sort_pos(a, 1) + sort_pos(b, 2) + sort_pos(c, 3) + sort_pos(d, 4) )
# Extract the values from the original arrays in their new order
combined.sort_by { |zipped| zipped[1] }.map { |zipped| zipped[0] }
=> [:g, :d, :a, :h, :e, :i, :b, :f, :j, :c]
There might be a cleaner way of doing this in Ruby . . . but I think the end result is what you are after - an "even" mix of multiple arrays.
If you only care about even-ness of mix from a statistical perspective (i.e. over time it is "fair"), you could just do this:
(a+b+c+d).shuffle
=> [:g, :b, :i, :c, :a, :h, :e, :j, :f, :d]
Related
Would anyone be so kind to explain to me how I finish my recursive binary search problem? The recursive aspect is confusing me. I would love for an explanation on what thats doing if possible!!! I think I need to increment the 'half' value I have within the if or elsif but I don't know what it would look like. Please suggest ways to add to the code I currently have rather than refactor to something simpler... at least at first! Thanks!
def binary_search(letter, array)
half = (array.length - 1)/2
if letter == array[half]
return half
end
if letter > array[half] && letter <= array[-1]
array = array[half...array.length]
binary_search(letter, array)
elsif letter < array[half] && letter >= array[0]
array = array[0...half]
binary_search(letter, array)
else
nil
end
end
arr = [:A, :B, :C, :D, :E, :F, :G]
p binary_search(:C, arr)
half was part of the problem. With a length of 2, half would be 0, and you would "split" your array in a full array and an empty array : recursion would never end.
You also need to keep an index, and add half to it when you consider the 2nd Array :
def binary_search(letter, array, i=0)
puts "Been here for #{array} with #{i}"
half = array.length / 2
if letter == array[half]
return i + half
end
if letter > array[half] && letter <= array[-1]
binary_search(letter, array.drop(half), i + half)
elsif letter < array[half] && letter >= array[0]
binary_search(letter, array.take(half), i)
else
nil
end
end
arr = [:A, :B, :C, :D, :E, :F, :G]
p binary_search(:C, arr)
p binary_search(:G, arr)
It outputs
Been here for [:A, :B, :C, :D, :E, :F, :G] with 0
Been here for [:A, :B, :C] with 0
Been here for [:B, :C] with 1
2
Been here for [:A, :B, :C, :D, :E, :F, :G] with 0
Been here for [:D, :E, :F, :G] with 3
Been here for [:F, :G] with 5
6
I have a string that I need to convert into a hash. The string's key will always be a symbol and the value will always be an integer:
"a=1, b=2, c=3, d=4"
This string should return a hash that looks like:
{ :a => 1, :b => 2, :c => 3, :d => 4 }
I've tried several different things, but the closest I've been able to come so far is to split the string twice, first for the comma and space, second for the equal sign, and create symbols:
def str_to_hash(str)
array = str.split(', ').map{|str| str.split('=').map{|k, v| [k.to_sym, v] }}
end
I'd expected the following output:
{:a=>1, :b=>2, :c=>3, :d=>4}
Instead I got:
[[[:a, nil], [:"1", nil]], [[:b, nil], [:"2", nil]], [[:c, nil], [:"3", nil]], [[:d, nil], [:"4", nil]]]
As you can see, it is creating 8 separate strings with 4 symbols. I can't figure out how to make Ruby recognize the numbers and set them as the values in the key/value pair. I've looked online and even asked my coworkers for help, but haven't found an answer so far. Can anybody help?
Try this I think it looks a little cleaner
s= "a=1, b=2, c=3, d=4"
Hash[s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}]
Here is the inner workings
#utilize scan with capture groups to produce a multidimensional Array
s.scan(/(\w)=(\d)/)
#=> [["a", "1"], ["b", "2"], ["c", "3"], ["d", "4"]]
#pass the inner Arrays to #map an replace the first item with a sym and the second to Integer
.map{|a,b| [a.to_sym,b.to_i]}
#=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
#Wrap the whole thing in Hash::[] syntax to convert
Hash[s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}]
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
If you want to avoid the Hash::[] method which I have always though was ugly you can do the following
#Ruby >= 2.1 you can use Array#to_h
s.scan(/(\w)=(\d)/).map{|a,b| [a.to_sym,b.to_i]}.to_h
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
#Ruby < 2.1 you can use Enumerable#each_with_object
s.scan(/(\w)=(\d)/).each_with_object({}){|(k,v),obj| obj[k.to_sym] = v.to_i}
#=> {:a=>1, :b=>2, :c=>3, :d=>4}
Although there are a ton of other ways to handle this issue as is evident by the many other answers here is one more just for fun.
Hash[*s.scan(/(\w)=(\d)/).flatten.each_with_index.map{|k,i| i.even? ? k.to_sym : k.to_i}]
> s = "a=1, b=2, c=3, d=4"
=> "a=1, b=2, c=3, d=4"
> Hash[s.split(",").map(&:strip).map { |p| p.split("=") }.map { |k, v| [ k.to_sym, v.to_i ] }]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
Part of the problem is that you're trying to do it in a single line and losing track of what the intermediate values are. Break it down into each component, make sure you're using what Ruby gives you, etc.
Your naming assumes you get an array back (not a hash). Hash[...], however, will create a hash based on an array of [key, value] pairs. This makes manual hash stuffing go away. Also, that method should return a hash, not set something–keep methods small, and pure.
Note I strip the first set of split values. This avoids symbols like :" a", which you get if you don't trim leading/trailing spaces. My code does not take strings like "a = 1" into account–yours should.
First, make things readable. Then, if (and only if) it makes sense, and remains legible, play code golf.
> s = "a=1, b=2, c=3, d=4"
=> "a=1, b=2, c=3, d=4"
> a1 = s.split(",")
=> ["a=1", " b=2", " c=3", " d=4"]
> a2 = a1.map(&:strip)
=> ["a=1", "b=2", "c=3", "d=4"]
> a3 = a2.map { |s| s.split("=") }
=> [["a", "1"], ["b", "2"], ["c", "3"], ["d", "4"]]
> a4 = a3.map { |k, v| [ k.to_sym, v.to_i ] }
=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
> Hash[a4]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
Unrelated, but if you're doing a lot of ETL with Ruby, especially on plain text, using mixins can make code much cleaner, closer to a DSL. You can play horrible games, too, like:
> def splitter(sym, s)
String.send(:define_method, sym) do
split(s).map(&:strip)
end
end
> s = "a=1, b=2, c=3, d=4"
> splitter :split_comma, ","
> splitter :split_eq, "-"
> Hash[s.split_comma.map(&:split_eq).map { |k, v| [ k.to_sym, v.to_i ]}]
=> {:a=>1, :b=>2, :c=>3, :d=>4}
It can get significantly worse than this and become a full-fledged ETL DSL. It's great if you need it, though.
Have you tried Hash['a',1,'b',2,'c',3] ??
On your irb terminal it should give this => {"a"=>1, "b"=>2, "c"=>3}
So all that you can do is split the string and give it to Hash which will do the job for you.
Hash[s.split(",").map(&:strip).map{|p| x = p.split("="); [x[0].to_sym, x[1]]}]
Hope that helps
a little hackhish but it works, you can take it from here :-)
h = {}
"a=1, b=2, c=3, d=4".split(',').each do |fo|
k = fo.split('=').first.to_sym
v = fo.split('=').last
h[k] = v
end
puts h.class.name
puts h
My solution:
string = "a=1, b=2, c=3, d=4"
hash = {}
string.split(',').each do |pair|
key,value = pair.split(/=/)
hash[key] = value
end
puts hash.inspect
Despite from not being a one-linner it's a readable solution.
What is the easiest way to rearrange the elements of an array by referencing their indice? Something like this:
[:a, :b, :c, :d].rearrange(3, 1, 0, 2) # => [:d, :b, :a, :c]
[:a, :b, :c, :d].rearrange!(3, 1, 0, 2) # => [:d, :b, :a, :c]
Well, Ruby already has this method, only that it's called values_at:
>> [:a, :b, :c, :d].values_at(3, 1, 0, 2)
=> [:d, :b, :a, :c]
I'd turn it around a bit. A single permutation is just a function which maps an Array to another Array so this seems natural:
a = [:a, :b, :c, :d]
permuted = [3, 1, 0, 2].map { |i| a[i] }
That's easy to monkey patch into Array if necessary:
class Array
def permute(*p)
p.map { |i| self[i] }
end
end
Error checking, permute!, and further niceties are left as an exercise.
I have an array: x = [a, b, c, d, e, f, g, h] which can have objects from 1 to 9
Firstly, I have to count IF any of these objects is present 3 times. I don't want to write
if (x.count(1) == 3) or (x.count(2) == 3) ...etc...
is there a way to shorten this, like below?
x.count { |obj| obj } == 3
Secondly, if I know that an object has been found with 3 instances, how can I find out which one was it? (1 or 2 or 3.....)
x = [:a, :b, :b, :b, :c, :c, :c]
counted = Hash[
x.group_by do |e|
x.count(e)
end.map do |count, items|
[count, items.uniq]
end
]
p counted[3] #=> [:b, :c]
How does this work? Let's follow the steps. First, let's group the items by count:
grouped_by_count = x.group_by do |e|
x.count(e)
end
This produces a hash with the keys being the counts, and the values being the list of non-unique items having that count:
p grouped_by_count
#=> {1=>[:a], 3=>[:b, :b, :b, :c, :c, :c]}
We'd really rather have unique items, though, so let's do that transform:
grouped_by_count_unique = grouped_by_count.map do |count, items|
[count, items.uniq]
end
p grouped_by_count_unique
#=> [[1, [:a]], [3, [:b, :c]]]
That gives us an array of arrays, and not a hash. Fortunately, it's easy to turn an array of arrays into a hash:
counted = Hash[grouped_by_count_unique]
p counted
# => {1=>[:a], 3=>[:b, :c]}
Now just put the pieces together eliminating the temporaries and you get the answer at the top.
I have a factorised array of n dimensions and I would like to develop it.
Here is an example:
develop([:a, :aa]) #=> [[:a, :aa]]
...which is the same as: [:a].product([:aa]).
Or, more complicated:
develop([:a, [:aa, :bb]]) #=> [[:a, :aa],
[:a, :bb]]
I'm working with Ruby 1.9. Thank you for any idea.
Edit:
Another example, with 3 levels of embedded arrays:
develop([:a, [[:b, [:ba, :bb]],
[:c, [:ca, :cb]],
[:d, [:da, :db]]]]) #=> [[:a, :b, :ba],
[:a, :b, :bb],
[:a, :c, :ca],
[:a, :c, :cb],
[:a, :d, :da],
[:a, :d, :db]]
I wonder if we could use Array's product method (http://ruby-doc.org/core-1.9.3/Array.html#method-i-product), even if we have some embedded arrays.
I'm not sure I fully understand what you are trying to do to these poor arrays, but I managed to make a function that gives the correct output for both the cases you specified. Here is the complete code:
def develop(x)
return x unless x.is_a? Array
y = []
x[1].each do |s|
d = develop(s)
d = [d] unless d.is_a? Array
d.each do |t|
t = [t] unless t.is_a? Array
y << [x.first] + t
end
end
return y
end
x = [:a,
[
[:b, [:ba, :bb]],
[:c, [:ca, :cb]],
[:d, [:da, :db]]
]
]
p develop(x)
p develop [:a, [:aa, :bb]]
The output is:
C:\Users\David\Documents\scraps\test_ruby>ruby test.rb
[[:a, :b, :ba], [:a, :b, :bb], [:a, :c, :ca], [:a, :c, :cb], [:a, :d, :da], [:a, :d, :db]]
[[:a, :aa], [:a, :bb]]
EDIT 1: Here's a shorter version that also gives the right output:
def develop(x)
return [x] unless x.is_a? Array
Array(x.last).collect do |s|
develop(s).collect do |t|
[x.first] + Array(t)
end
end.flatten 1
end