Remove array elements at indices non destructively - ruby-on-rails

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"]

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 }

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}

Ruby find combination

I am trying to take input as a string.
Then I need to find all the possible combination and distinct combination but I am unable to do so.
input = "aabb"
Output I need to print all Combination =
'a','a','b','b','aa','ab','bb','aab','abb','aabb'
Now Distinct combination
'a','b','aa','ab','bb','aab','abb','aabb'
Then I need to count the letters and do a summation
'a','a','b','b','aa','ab','bb','aab','abb','aabb'
For this
result = 1+1+1+1+2+2+2+3+3+4
Similarly for the other combination I need to find summation.
You can use Array#combination.
To get all combinations:
input = "aabb"
res = []
input.size.times { |n| res << input.chars.combination(n+1).map { |a| a.join } }
res.flatten
#=> ["a", "a", "b", "b", "aa", "ab", "ab", "ab", "ab", "bb", "aab", "aab", "abb", "abb", "aabb"]
distinct combinations:
res.flatten.uniq
#=> ["a", "b", "aa", "ab", "bb", "aab", "abb", "aabb"]
to count the letters and do a summation:
res.flatten.uniq.map(&:size)
#=> [1, 1, 2, 2, 2, 3, 3, 4]
res.flatten.uniq.map(&:size).reduce(:+)
# => 18
To get all the substrings of your input (or more generally to get all subsequences of an Enumerable) you can use something like this:
def subsequences(e)
a = e.to_a
indices = (0..a.length - 1).to_a
indices.product(indices)
.reject { |i, j| i > j }
.map { |i, j| a[i..j] }
end
You would use that on your string like this: subsequences(input.chars).map(&:join). The chars and join are only necessary because Strings are not Enumerable, but the subsequences function does not really need that. You can just take out the first line and it should still work for strings (anything that has a "slicing" subscript operator, really ...).
Note also that this is not the only way to do this. The basic problem here is to iterate over all ordered pairs of indices of a sequence. You could also do that with basic loops. I just happen to find the cartesian product method very elegant. ;)
Once you have your first list in a variable, say list, the second task is as easy as list.uniq, and the third one is solved by
list.map(&:size).reduce(:+)

How do you double number in ruby for an iterator

Need to double each value in my array. I know double is not a command, but not sure what else to use.
odds = [1,3,5,7,9]
array.each do |x|
x += double
print "#{x}"
end
Use Array#map to create a new array
odds = [1,3,5,7,9]
arr = odds.map{|x| x*2}
arr.inspect
# => [2,6,10,14,18]
To modify the same array use Array#map!
odds = [1,3,5,7,9]
odds.map!{|x| x*2}
odds.inspect
# => [2,6,10,14,18]
Do you mean 'double' as in multiply by 2, or double as in duplicate?
array = [1,3,5,7,9]
array.each { |x| print "#{x*2}"; }
But you probably want either a new array, or to map your existing array,
result = []
result = array.map { |x| x*2 }
#or
result = array.map! { |x| x*2 }
Here is an example of duplicate,
result = []
array.map { |x| result << x; result << x; } #duplicate
see here: http://www.natontesting.com/2011/01/01/rubys-each-select-and-reject-methods/
Doing the Simplest Thing Possible
The simplest thing to do is to simply iterate over your array with Array#map and Kernel#p. For example:
odds = [1, 3, 5, 7, 9]
odds.map { |i| p i*2 }
Defining a Custom Method
If you need more control, you can create a custom method that handles a block or returns an enumerator. For example:
def double *array
array.flatten!
block_given? ? array.map { |i| yield i*2 } : array.map { |i| i*2 }.to_enum
end
enumerator = double 1, 2, 3
#=> #<Enumerator: ...>
enumerator.each { |i| p i }
#=> [2, 4, 6]
double(1, 2, 3) { |i| p i }
#=> [2, 4, 6]
This is probably overkill for your use case, but it's a useful technique to know if you want to work with enumerators and blocks. Hope it helps!
The way that I have found to do it is to times / equal it by 2. For example:
odds = [1,3,5,7,9]
odds.each do |x|
x *= 2
print x
end

Ruby mixed array to nested hash

I have a Ruby array whose elements alternate between Strings and Hashes. For example-
["1234", Hash#1, "5678", Hash#2]
I would like to create a nested hash structure from this. So,
hash["1234"]["key in hash#1"] = value
hash["5678"]["key in hash#2"] = value
Does anyone have/now a nice way of doing this? Thank you.
Simply use
hsh = Hash[*arr] #suppose arr is the array you have
It will slice 2 at a time and convert into hash.
I don't think there is a method on array to do this directly. The following code works and is quite easy to read.
hsh = {}
ary.each_slice(2) do |a, b|
hsh[a] = b
end
# Now `hsh` is as you want it to be
Guessing at what you want, since "key in hash#1" is not clear at all, nor have you defined what hash or value should be:
value = 42
h1 = {a:1}
h2 = {b:2}
a = ["1234",h1,"5678",h2]
a.each_slice(2).each{ |str,h| h[str] = value }
p h1, #=> {:a=>1, "1234"=>42}
h2 #=> {:b=>2, "5678"=>42}
Alternatively, perhaps you mean this:
h1 = {a:1}
h2 = {b:2}
a = ["1234",h1,"5678",h2]
hash = Hash[ a.each_slice(2).to_a ]
p hash #=> {"1234"=>{:a=>1}, "5678"=>{:b=>2}}
p hash["1234"][:a] #=> 1
let's guess, using facets just for fun:
require 'facets'
xs = ["1234", {:a => 1, :b => 2}, "5678", {:c => 3}]
xs.each_slice(2).mash.to_h
#=> {"1234"=>{:a=>1, :b=>2}, "5678"=>{:c=>3}}

Resources