Appending n elements to an array - ruby

I have an array and a hash
L = []
H = {3=>"a", 2=>"b", 1=>"c"}
And so I will iterate over the keys to get the number of times n the element occurs and append that element to an array n times
Result
L = ['a', 'a', 'a', 'b', 'b', 'c']
What's a nice way to write this with inject (or other methods that I often see in ruby code)?

array = hash.flat_map { |k,v| [v]*k }

#David's answer is spot-on for your needs. In general, however, you can add an object o to an existing array n times via one of:
# Modify the array in-place, or…
my_array.concat( [o]*n )
# …alternatively create a new modified array
new_array = my_array + [o]*n

Related

How to remove all the elements after a particular index

Given:
n = 2
arr = %w(10 20 30 40 50)
This is a way to remove all the array elements after index n:
arr.delete_if {|num| arr.index(num) > n }
# => ["10", "20", "30"]
Is there any other way to remove all the elements from an array without iterating over and comparing the condition?
Array#[]= operator comes in handy:
arr[n+1 .. -1] = []
In Ruby 2.6, you can also use endless ranges:
arr[n+1 ..] = []
Note that your code is slow, as it searches the array for every element and is thus O(n^2), but also wrong if the array contains repeating elements. For example, with arr = %w(10 20 30 10 20), your code does not change the array. This would be your code, only faster and correct (O(n); though the #[]= solution above is even faster and more straightforward):
arr.delete_if.with_index { |num, idx| idx > n }
You are deleting elements having index greater than n=2 & get rest of array where you can simply get it as,
arr = arr[0..2]
Above will regenerate new array and arr reference will point to it.
Update: going little deep with point claimed by Cary Swoveland in comment,
Array can be updated without initialising new one as follow,
# This one will have same object id or we can say, do not point to new array
arr.replace(arr[0,3])
You can use Array#slice! to remove elements within a certain index range, e.g.:
arr = %w(10 20 30 40 50)
arr.slice!(3..-1) #=> ["40", "50"]
arr #=> ["10", "20", "30"]
Note that slice! also returns the removed portion.

Iterate two collection at same time

a = [1,2,3]
b = [4,5 ]
What I want is to iterate these two collection at same time and do something with iterator, the pseudo code would be like:
for i in a
for j in b
collect i * j
when one collection runs out of element, the loop stops.
the result will be [4, 10]
What I have is this:
a = [1,2,3]
b = [4,5 ]
a.zip(b).reject { |c| c.any? { |d| d.nil? } }.map { |e| e.reduce(&:*) }
Any better solution? Thanks!
And The perfect solution I am looking for is to match the intent of my pseudo code.
You can do this:
a, b = b, a if b.length < a.length
a.zip(b).map { |ia, ib| ia * ib }
# => [4, 10]
The first line makes sure that array a has at most the same number of elements as array b. This is because zip creates an array of arrays of the length of the called array. Having a as the shortest array makes sure that there would be no nils.
Here is another way to do it:
[a.length, b.length].min.times.map {|i| a[i]*b[i] }
The idea is that you take the shorter of the two array lengths, [a.length, b.length].min, and you iterate that many times over an integer, i, which you use as an index into the arrays.

Im trying to delete a group of items inside an array in between two points, literally matching the giving points

Let's say that u have a given array and the idea is to get rid of everything in between these two poins: 'a' and 'e', The result will be a new array but starting with 'a' like this ex:
arr = %w(a b c d e f j k)
and the outcome will be:
arr['a', 'f', 'j, 'k']
How would u make the check-function for this challenge?
Get the array index of the first element you want to count:
start = array.index('a')
Get the array index of the last elementoyou want to count:
end = array.rindex('e')
Then split the array inside those elements:
array = %w(a b c d e f j k)[start, end]
That should work.
Reference:
Get index of array element faster than O(n)

extracting from 2 dimensional array and creating a hash with array values

I have a 2 dimensional array
v = [ ["ab","12"], ["ab","31"], ["gh","54"] ]
The first element of the subarray of v will have repeating elements, such as "ab". I want to create a hash that puts the key as the first element of the subarray, and values as an array of corresponding second elements from v.
please advice.
Further, I want this, h={"ab"=>["12","31"],"gh"=>["54"]} and then I want to return h.values, such that the array [["12","31"],["54"]] is returned
v.inject(Hash.new{|h,k|h[k]=[]}) { |h, (k, v)| h[k] << v ; h}
What it does:
inject (also called reduce) is a fold. Wikipedia defines folds like this: "a family of higher-order functions that analyze a recursive data structure and recombine through use of a given combining operation the results of recursively processing its constituent parts, building up a return value".
The block form of Hash.new takes two arguments, the hash itself and the key. If your default argument is a mutable object, you have to set the default this way, otherwise all keys will point to the same array instance.
In inject's block, we get two arguments, the hash and the current value of the iteration. Since this is a two element array, (k, v) is used to destructure the latter into two variables.
Finally we add each value to the array for its key and return the entire hash for the next iteration.
v.inject({­}) do |res,­ ar|
res[ar.fir­st] ||= []
res[ar.fir­st] << ar.la­st
res
end
v = [ ["ab","12"], ["ab","31"], ["gh","54"] ]
This gets you a hash, where the keys are the
unique first elements from the sub arrays.
h = v.inject({}) { |c,i| (c[i.first] ||= []) << i.last; c }
This turns that hash back into an array, just in case you need the array of arrays format.
arr = h.collect { |k,v| [k,v] }

Get index of array element faster than O(n)

Given I have a HUGE array, and a value from it. I want to get index of the value in array. Is there any other way, rather then call Array#index to get it? The problem comes from the need of keeping really huge array and calling Array#index enormous amount of times.
After a couple of tries I found that caching indexes inside elements by storing structs with (value, index) fields instead of the value itself gives a huge step in performance (20x times win).
Still I wonder if there's a more convenient way of finding index of en element without caching (or there's a good caching technique that will boost up the performance).
Why not use index or rindex?
array = %w( a b c d e)
# get FIRST index of element searched
puts array.index('a')
# get LAST index of element searched
puts array.rindex('a')
index: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-index
rindex: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-rindex
Convert the array into a hash. Then look for the key.
array = ['a', 'b', 'c']
hash = Hash[array.map.with_index.to_a] # => {"a"=>0, "b"=>1, "c"=>2}
hash['b'] # => 1
Other answers don't take into account the possibility of an entry listed multiple times in an array. This will return a hash where each key is a unique object in the array and each value is an array of indices that corresponds to where the object lives:
a = [1, 2, 3, 1, 2, 3, 4]
=> [1, 2, 3, 1, 2, 3, 4]
indices = a.each_with_index.inject(Hash.new { Array.new }) do |hash, (obj, i)|
hash[obj] += [i]
hash
end
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5], 4 => [6] }
This allows for a quick search for duplicate entries:
indices.select { |k, v| v.size > 1 }
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5] }
Is there a good reason not to use a hash? Lookups are O(1) vs. O(n) for the array.
If your array has a natural order use binary search.
Use binary search.
Binary search has O(log n) access time.
Here are the steps on how to use binary search,
What is the ordering of you array? For example, is it sorted by name?
Use bsearch to find elements or indices
Code example
# assume array is sorted by name!
array.bsearch { |each| "Jamie" <=> each.name } # returns element
(0..array.size).bsearch { |n| "Jamie" <=> array[n].name } # returns index
If it's a sorted array you could use a Binary search algorithm (O(log n)). For example, extending the Array-class with this functionality:
class Array
def b_search(e, l = 0, u = length - 1)
return if lower_index > upper_index
midpoint_index = (lower_index + upper_index) / 2
return midpoint_index if self[midpoint_index] == value
if value < self[midpoint_index]
b_search(value, lower_index, upper_index - 1)
else
b_search(value, lower_index + 1, upper_index)
end
end
end
Taking a combination of #sawa's answer and the comment listed there you could implement a "quick" index and rindex on the array class.
class Array
def quick_index el
hash = Hash[self.map.with_index.to_a]
hash[el]
end
def quick_rindex el
hash = Hash[self.reverse.map.with_index.to_a]
array.length - 1 - hash[el]
end
end
Still I wonder if there's a more convenient way of finding index of en element without caching (or there's a good caching technique that will boost up the performance).
You can use binary search (if your array is ordered and the values you store in the array are comparable in some way). For that to work you need to be able to tell the binary search whether it should be looking "to the left" or "to the right" of the current element. But I believe there is nothing wrong with storing the index at insertion time and then using it if you are getting the element from the same array.

Resources