Convert array to single object if array is a single object - ruby-on-rails

Really simple question.
Is there a method to do the following?
["a"] => "a"
[1] => 1
[1,"a"] => [1, "a"]
i.e. if an array is a single object, return the object, otherwise return the array.
Without doing something ugly like
array.length == 1 ? array[0] : array

basically you should stick to what you wrote - it's simple and does what it should.
of course you may always monkey patch the Array definition... (unrecommended, but it does what you expect)
class Array
def first_or_array
length > 1 ? self : self[0]
end
end
[1].first_or_array # 1
[1, 2].first_or_array # [1, 2]

Related

How to replace missing key/value with zero using slice

I have a hash. I need to extract some key/value pairs, but some desired keys are missing.
How can I replace the missing pairs with "key" => 0.0 when I call attributes.slice on the record and keys as follows:
record = Model.last
record.attributes.slice('k1','k2','k3','k4','k5') # this returns
=> {"k1"=> 343, k3=> 0.0}
If some keys are missing then they won't appear in the result. How can I get the remaining missing keys assigned with 0.0?
Suppose
h = { 'k2'=>2, 'k1'=>1 }
and
all_keys = ['k1', 'k2', 'k3', 'k4']
then
all_keys.map { |k| h.fetch(k,0.0) }
#=> [1, 2, 0.0, 0.0]
See Hash#fetch.
We can take advantage of the fact that a hash can be overwritten with new key/value pairs:
{a: 0, b: 0}.merge(a: 2) # => {:a=>2, :b=>0}
Knowing that, we can do something like this:
desired_keys = [:a, :b]
foo = {a: 1}
desired_keys.zip([0] * desired_keys.size).to_h.merge(foo.slice(*desired_keys))
# => {:a=>1, :b=>0}
desired_keys is a predefined list of the key/value pairs we want, foo is the actual hash the real values are coming from.
[0] * 2 # => [0, 0] creates an array of a given size.
desired_keys.zip([0] * desired_keys.size).to_h creates a temporary hash of the values being used as filler.
merge(foo.slice(*desired_keys)) grabs the pairs we wanted. In this situation, * AKA "splat" explodes the array into its individual elements, so they're passed as separate parameters to slice. Here's what's happening:
def bar(*a)
a
end
bar(%w[a b]) # => [["a", "b"]]
bar(*%w[a b]) # => ["a", "b"]
Notice that the first call is passing in an array, whereas the second passes separate values.
Breaking it down a little to make it a bit more apparent:
desired_keys.zip([0] * desired_keys.size).to_h # => {:a=>0, :b=>0}
.merge(foo.slice(*desired_keys)) # => {:a=>1, :b=>0}
Because we know the record fields we're retrieving, it's easy to create that temporary hash once, in advance, then reuse it every time, resulting in very fast code:
DESIRED_KEYS = [:a, :b]
ZERO_HASH = DESIRED_KEYS.zip([0] * DESIRED_KEYS.size).to_h # => {:a=>0, :b=>0}
foo = {a: 1}
ZERO_HASH.merge(foo.slice(*DESIRED_KEYS))
# => {:a=>1, :b=>0}
All the methods, including * are part of Array or Hash.
How to use 0.0 instead of 0 is left as an exercise for the reader.
I think this should work in your case
Model.slice('k1','k2','k3','k4','k5').transform_values! { |v| v ? v : 0.0 }

Remove array elements at indices non destructively

Let's say you have something like this:
my_array = ['some_rather', 'long_named', 'array_element', 'entry']
I want to remove arbitrary entries by index from my_array without changing it and I want the filtered (i.e. array with indices removed) to be returned from my call. Furthermore, I want to avoid chaining 4 separate calls and write a block doing so.
Example:
filtered_array = my_array.drop_indices(1,3)
You could chain Enumerable's with_index onto Array's reject method to do what you want, though this might violate your desire to not chain separate method calls or write a block to do this:
my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]
filtered = my_array.reject.with_index { |_, index| indices_to_remove.include?(index) }
p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]
If this isn't acceptable, the only other thing I can think of right now, to keep duplicate items (as noted in my comment to your solution), is to change from indices_to_remove to indices_to_keep:
my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]
indices_to_keep = [*(0...my_array.length)] - indices_to_remove
filtered = my_array.values_at(*indices_to_keep)
p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]
For Arrays with duplicate Elements the best I know is:
array = [0,15,8,15,8]
indices_to_remove = [1,4]
res = array.reject.with_index{ |_,i| indices_to_remove.include?(i) }
returns
[0,8,15]
Additionally for arrays with unique entries such as a set of users
(using variable definitions from the question)
filtered_array = my_array - my_array.values_at(1,3)
Bonus, if your indices are inside an array themselves:
indices_to_remove = [1,3]
filtered_array = my_array - my_array.values_at(*indices_to_remove)
I think this is rather descriptive, not awkward and not shorter than needed.
One more possible solution with some addition, now it's also will work with negative indexes:
array = %w( a b c d e f )
indexes = [1, 2, -1, -9, -6, 6]
def array_except(array, *indexes)
indexes = indexes.map { |e| e.negative? ? e + array.length : e }
array.values_at(*((0...array.length).to_a - indexes))
end
array_except(array, *indexes)
=> ["d", "e"]
array_except(array, 0, -1)
=> ["b", "c", "d", "e"]

Using .map function to create hashes

I have an array [5,2,6,4] and I would like to create a structure such as the first minus the second etc until the last row.
I have tried using map, but not sure how to proceed since i might need indxes.
I would like to store the result in something that looks like:
{1 => (5, 2, 3), 2 =>(2,6,-4), 3 => (6,4,2)}
So an array of x should return x-1 hashes.
Anybody knows how to do? should be a simple one.
Thank you.
First, you want to work with the array elements in pairs: 5,2, 2,6, ... That means you want to use each_cons:
a.each_cons(2) { |(e1, e2)| ... }
Then you'll want the index to get the 1, 2, ... hash keys; that suggests throwing a Enumerator#with_index into the mix:
a.each_cons(2).with_index { |(e1, e2), i| ... }
Then you can use with_object to get the final piece (the hash) into play:
a.each_cons(2).with_index.with_object({}) { |((e1, e2), i), h| h[i + 1] = [e1, e2, e1 - e2] }
If you think all the parentheses in the block's arguments are too noisy then you can do it in steps rather than a single one-liner.
You can use each_index:
a = [5, 2, 6, 4]
h = {}
a[0..-2].each_index { |i| h[i+1] = [a[i], a[i+1], a[i] - a[i+1]] }
h
=> {1=>[5, 2, 3], 2=>[2, 6, -4], 3=>[6, 4, 2]}
Try to use
each_with_index
Suppose you have an array:
arr = [3,[2,3],4,5]
And you want to covert with hash(key-value pair). 'Key' denotes an index of an array and 'value' denotes value of an array. Take a blank hash and iterate with each_with_index and pushed into the hash and finally print the hash.
Try this:
hash={}
arr.each_with_index do |val, index|
hash[index]=val
end
p hash
Its output will be:
{0=>3, 1=>[2, 3], 2=>4, 3=>5}
If you want that index always starts with 1 or 2 etc then use
arr.each.with_index(1) do |val, index|
hash[index] = val
end
Output will be:
{1=>3, 2=>[2, 3], 3=>4, 4=>5}

How to get the max of an arbitrary function in Ruby?

Consider some function foo:
def foo(input)
input * 2
end
How to get the max value of input for some array a?
a = [3, 5, 7, 9, 6]
Something like the following (which doesn't work) should return 9:
a.max do |value|
foo(value)
end
How to do it?
Ruby 1.9.2
You need max_by , not max. http://www.ruby-doc.org/core-1.9.3/Enumerable.html#method-i-max_by
max:
Returns the object in enum with the maximum value. The first form
assumes all objects implement Comparable; the second uses the block to
return a <=> b.
a = %w(albatross dog horse)
a.max #=> "horse"
a.max {|a,b| a.length <=> b.length } #=> "albatross"
So max does take a block, but it doesn't do what you expected it to.
max_by:
Returns the object in enum that gives the maximum value from the given
block.
If no block is given, an enumerator is returned instead.
a = %w(albatross dog horse)
a.max_by {|x| x.length } #=> "albatross"
use array map: a.map{|v|foo(v)}.max

push new element into array within hash

I have a hash which, I have keys that uniquely identify each element within the hash. And within each element, I have an array. So my question is, how do I put another element inside that array within the hash.
{"Apple"=>[1, 5.99], "Banana"=>[5, 9.99]}
I'm looping through a result set, and I'm a little bit lost how to add another element to the array...
If your hash is called, for example, hsh, then the "Apple" array can be accessed by hsh["Apple"]. You can use this like any variable, so to add a value to that array just do hsh["Apple"] << some_value. Like so:
irb> hsh = { "Apple" => [1, 5.99], "Banana" => [5, 9.99] }
irb> hsh["Apple"] << 9999
=> { "Apple" => [1, 5.99, 9999], "Banana" => [5, 9.99] }

Resources