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.
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
The code:
a = [1, 2, 3]
h = {a: 1}
def f args
p args
end
h.map(&method(:f))
a.map(&method(:f))
h.map do |k,v|
p [k,v]
end
The output:
[:a, 1]
1
2
3
[:a, 1]
Why can't I define f for a hash as follows?
def f k, v
p [k, v]
end
You are correct that the reason stems from the one of the two main differences between proc's and lambda's. I'll trying explaining it in a slightly different way than you did.
Consider:
a = [:a, 1]
h = {a: 1}
def f(k,v)
p [k, v]
end
a.each(&method(:f))
#-> in `f': wrong number of arguments (1 for 2) (ArgumentError)
h.each(&method(:f))
#-> in `f': wrong number of arguments (1 for 2) (ArgumentError)
where I use #-> to show what is printed and #=> to show what is returned. You used map, but each is more appropriate here, and makes the same point.
In both cases elements of the receiver are being passed to the block1:
&method(:f)
which is (more-or-less, as I will explain) equivalent to:
{ |k,v| p [k,v] }
The block is complaining (for both the array and hash) that it is expecting two arguments but receiving only one, and that is not acceptable. "Hmmm", the reader is thinking, "why doesn't it disambiguate in the normal way?"
Let's try using the block directly:
a.map { |k,v| p [k,v] }
#-> [:a, nil]
# [1, nil]
h.map { |k,v| p [k,v] }
#-> [:a, 1]
This works as expected, but does not return what we wanted for the array.
The first element of a (:a) is passed into the block and the block variables are assigned:
k,v = :a
#=> :a
k #=> :a
v #=> nil
and
p [k,v]
#-> :a
#-> nil
Next, 1 is passed to the block and [1,nil] is printed.
Let's try one more thing, using a proc created with Proc::new:
fp = Proc.new { |k,v| p [k, v] }
#=> #<Proc:0x007ffd6a0a8b00#(irb):34>
fp.lambda?
#=> false
a.each { |e| fp.call(e) }
#-> [:a, nil]
#-> [:a, 1]
h.each { |e| fp[e] }
#-> [:a, 1]
(Here I've used one of three aliases for Proc#call.) We see that calling the proc has the same result as using a block. The proc expects two arguments and but receives only one, but, unlike the lambda, does not complain2.
This tells us that we need to make small changes to a and f:
a = [[:a, 1]]
h = {a: 1}
def f(*(k,v))
p [k, v]
end
a.each(&method(:f))
#-> [:a, 1]
h.each(&method(:f))
#-> [:a, 1]
Incidentally, I think you may have fooled yourself with the variable name args:
def f args
p args
end
as the method has a single argument regardless of what you call it. :-)
1 The block is created by & calling Method#to_proc on the method f and then converting the proc (actually a lambda) to a block.
2 From the docs for Proc: "For procs created using lambda or ->() an error is generated if the wrong number of parameters are passed to a Proc with multiple parameters. For procs created using Proc.new or Kernel.proc, extra parameters are silently discarded."
As it appears, it must be some sort of implicit destructuring (or non-strict arguments handling), which works for procs, but doesn't for lambdas:
irb(main):007:0> Proc.new { |k,v| p [k,v] }.call([1,2])
[1, 2]
=> [1, 2]
irb(main):009:0> lambda { |k,v| p [k,v] }.call([1,2])
ArgumentError: wrong number of arguments (1 for 2)
from (irb):9:in `block in irb_binding'
from (irb):9:in `call'
from (irb):9
from /home/yuri/.rubies/ruby-2.1.5/bin/irb:11:in `<main>'
But one can make it work:
irb(main):010:0> lambda { |(k,v)| p [k,v] }.call([1,2])
[1, 2]
=> [1, 2]
And therefore:
def f ((k, v))
p [k, v]
end
So Hash#map always passes one argument.
UPD
This implicit destructuring also happens in block arguments.
names = ["Arthur", "Ford", "Trillian"]
ids = [42, 43, 44]
id_names = ids.zip(names) #=> [[42, "Arthur"], [43, "Ford"], [44, "Trillian"]]
id_names.each do |id, name|
puts "user #{id} is #{name}"
end
http://globaldev.co.uk/2013/09/ruby-tips-part-2/
UPD Don't take me wrong. I'm not suggesting writing such code (def f ((k, v))). In the question I was asking for explanation, not for the solution.
This is the input hash:
p Score.periods #{"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
This is my current code to exchange the keys with the values, while converting the keys to symbols:
periods = Score.periods.inject({}) do |hsh,(k,v)|
hsh[v] = k.to_sym
hsh
end
Here is the result:
p periods #{0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
It just seems like my code is clunky and it shouldn't take 4 lines to do what I'm doing here. Is there a cleaner way to write this?
You can do this:
Hash[periods.values.zip(periods.keys.map(&:to_sym))]
Or if you're using a version of Ruby where to_h is available for arrays, you can do this:
periods.values.zip(periods.keys.map(&:to_sym)).to_h
What the two examples above do is make arrays of the keys and values of the original hash. Note that the string keys of the hash are mapped to symbols by passing to_sym to map as a Proc:
periods.keys.map(&:to_sym)
# => [:q1, :q2, :q3, :q4, :h1, :h2]
periods.values
# => [0, 1, 2, 3, 4, 5]
Then it zips them up into an array of [value, key] pairs, where each corresponding elements of values is matched with its corresponding key in keys:
periods.values.zip(periods.keys.map(&:to_sym))
# => [[0, :q1], [1, :q2], [2, :q3], [3, :q4], [4, :h1], [5, :h2]]
Then that array can be converted back into a hash using Hash[array] or array.to_h.
The simplest way is:
data = {"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
Hash[data.invert.collect { |k, v| [ k, v.to_sym ] }]
The Hash[] method converts an array of key/value pairs into an actual Hash. Quite handy for situations like this.
If you're using Ruby on Rails this could be even easier:
data.symbolize_keys.invert
h = {"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
h.each_with_object({}) { |(k,v),g| g[v] = k.to_sym }
#=> {0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
The steps are as follows (for the benefit of Ruby newbies).
enum = h.each_with_object({})
#=> #<Enumerator: {0=>"q1", 1=>"q2", 2=>"q3", 3=>"q4",
# 4=>"h1", 5=>"h2"}:each_with_object({})>
The elements that will be generated by the enumerator and passed to the block can be seen by converting the enumerator to an array, using Enumerable#entries or Enumerable#to_a.
enum.entries
#=> [[["q1", 0], {}], [["q2", 1], {}], [["q3", 2], {}],
# [["q4", 3], {}], [["h1", 4], {}], [["h2", 5], {}]]
Continuing,
enum.each { |(k,v),g| g[v] = k.to_sym }
#=> {0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
In the last step, Enumerator#each passes the first element generated by enum to the block and assigns the three block variables. Consider the first element of enum that is passed to the block and the associated calculation of values for the three block variables. (I must first execute enum.rewind to reinitialize enum, as each above took the enumerator to its end. See Enumerator#rewind).
(k, v), g = enum.next
#=> [["q1", 0], {}]
k #=> "q1"
v #=> 0
g #=> {}
See Enumerator#next. The block calculation is therefore
g[v] = k.to_sym
#=> :q1
Hence,
g #=> {0=>:q1}
The next element of enum is passed to the block and similar calculations are performed.
(k, v), g = enum.next
#=> [["q2", 1], {0=>:q1}]
k #=> "q2"
v #=> 1
g #=> {0=>:q1}
g[v] = k.to_sym
#=> :q2
g #=> {0=>:q1, 1=>:q2}
The remaining calculations are similar.
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]
Given
a = [[:a, :b, :c]]
1) I understand this
a.each{|(x, y), z| p z} # => :b
that there are two variables (x, y) and z, so the third element :c is thrown away, and z matches :b. And I understand this
a.each{|(x, y), z| p y} # => nil
that (x, y) matches :a, and since it is not an array, there are no elements to it, and so y matches nil.
But how does
a.each{|(x, y), z| p x} # => :a
work? I expect nil to be returned.
2) Why are the return values like this?
a.each{|(x, y)| p x} #=> :a
a.each{|(x, y)| p y} #=> :b
I expect them to both return nil.
It's because of the syntax of parallel assignment.
a = [[:a, :b, :c]]
So a.each has only one element to iterate, which is [:a, :b, :c].
In the first case:
(x, y), z = [:a, :b, :c]
#=> x == :a, y == nil, z == :b
Here (x, y) is an array to match the first element :a, and x gets it, then z simply matches the second element :b.
And in the second case:
(x, y) = [:a, :b, :c]
#=> x == :a, y == :b
Here (x, y) as an entire array matches the array [:a, :b, :c], so x and y get :a and :b respectively.
This is just like requiring the "args + optional args (keyword args) + rest args" combination match provided arguments. It is just smart enough to take arguments by sequence.
Another smart example:
(a,b) = 1,2
=> [1, 2] # array match
#=> a == 1, b == 2
(a,b)=[1,2]
=> [1, 2] # array match
#=> a == 1, b == 2
In either case above, it will simply make the best guess on what it should take.