Ensuring unique elements in a nested ruby array - ruby

I have an array which can have N nested array where N can contain M arrays where both N and M are >= 1. Some examples include the following:
[[[1,2,3],[3,4,5]],[[2,1,1]]]
or
[[[1,2,3]],[]]]
and finally
[[[1,2,3],[3,4,5]],[[2,1,1]], [[1,1,1],[2,2,2]]]
I need something that returns a boolean true or false if there is a duplicate value for the 0th element in a nested array, the issue is the composite array is not the unique identifier. Only the 0th element in each value array such as [1,2,3] or [3,4,5], in this case the integers 1 and 3, are what I need a unique against. so in the case of the last array, [1,1,1] and [1,2,3] would clash as the 1 is repeated.
What's the best way to iterate through this type of nesting and signal true or false on whether there are duplicates?

def uniq_prime_elements?(arr)
prime_elements = arr.map(&:first).map(&:first).compact
prime_elements.length == prime_elements.uniq.length
end

Related

How to find max negative number and min positive number in array

Are there some methods that can find max negative number and min positive number in array?
Array contains no zeros
[-1,5,-4,6,1,8,-3].max_negative # => -1
[-1,5,-4,6,1,8,-3].min_positive # => 1
I don't think there's a built in solution. But you can achieve that very simply.
[-1,5,-4,6,1,8,-3].select{|n| n < 0}.max # => -1
[-1,5,-4,6,1,8,-3].select{|n| n >= 0}.min # => 1
You can even wrap them in a method if you want, maybe in array class.
[-1,5,-4,6,1,8,-3].select(&:negative?).max
[-1,5,-4,6,1,8,-3].select(&:positive?).min
neg_max, pos_min = [-1,5,-4,6,1,8,-3].minmax_by{|el| 1.0/el}
Here is one more way to do this - partition the array into positive and negative sub-arrays and then find max/min from each of those two arrays.
positives, negatives = arr.partition(&:positive?)
p positives.min
#=> 1
p negatives.max
#=> -1
Alternatively, you could do as below, where array is sorted and a pair is found where first element of pair is negative and second element is positive, thus giving us the values of max negative and min positive values.
max_neg, min_pos = arr.sort.each_slice(2)
.select {|i, j| i.negative? and j.positive?}.flatten

Return array a if given number is in it, return array a and the given number if it is not

Here's what I thought:
def appendUnique(a,x)
for i in 0 .. a.size-1 do
if a[i]=x then
a==a
else
a=a+x
end
p(a)
end
end
appendUnique([-1,5,3],4)
Compare each member of a with x, if a equals x, return a, else return a+x. Why doesn't this work? It just replaces all array members with 4s...
I want this: result [-1, 5, 3, 4] from the above since 4 isn't in the array and [-1, 5, 3] from appendUnique([-1,5,3],5).
There are several issues with your code:
in Ruby we usually use each instead of for to iterate collections
a[i] = x is an assignment, you want a[i] == x
a == a just returns true
a + x concatenates two arrays, but x is not an array
I would simply use Array#include? to check if the item is present:
def appendUnique(array, item)
if array.include? item
array
else
array + [item]
end
end
If you want an array with unique elements you can use Set class
It just replaces all array members with 4s...
a[i]=x is an assignment rather than comparison. Running this in a loop, as you do, would set every element of a to x (which is 4).
The rest of the code needs quite a lot of work too. For example: you should only be appending to a after you've run the loop and have established that x isn't in the array.

Finding the indexes of specific strings in an array, using a differently ordered equivalent array, ruby

I have two arrays: fasta_ids & frags_by_density. Both contain the same set of ≈1300 strings.
fasta_ids is ordered numerically e.g. ['frag1', 'frag2', 'frag3'...]
frags_by_density contains the same strings ordered differently e.g. ['frag14', 'frag1000'...]
The way in which frag_by_density is ordered is irrelevant to the question (but for any bioinformaticians, the 'frags' are contigs ordered by snp density).
What I want to do is find the indexes in the frag_by_density array, that contain each of the strings in fasta_ids. I want to end up with a new array of those positions (indexes), which will be in the same order as the fasta_ids array.
For example, if the order of the 'frag' strings was identical in both the fasta_ids and frags_by_density arrays, the output array would be: [0, 1, 2, 3...].
In this example, the value at index 2 of the output array (2), corresponds to the value at index 2 of fasta_ids ('frag3') - so I can deduce from this that the 'frag3' string is at index 2 in frags_by_density.
Below is the code I have come up with, at the moment it gets stuck in what I think is an infinite loop. I have annotated what each part should do:
x = 0 #the value of x will represent the position (index) in the density array
position_each_frag_id_in_d = [] #want to get positions of the values in frag_ids in frags_by_density
iteration = []
fasta_ids.each do |i|
if frags_by_density[x] == i
position_each_frag_id_in_d << x #if the value at position x matches the value at i, add it to the new array
iteration << i
else
until frags_by_density[x] == i #otherwise increment x until they do match, and add the position
x +=1
end
position_each_frag_id_in_d << x
iteration << i
end
x = iteration.length # x should be incremented, however I cannot simply do: x += 1, as x may have been incremented by the until loop
end
puts position_each_frag_id_in_d
This was quite a complex question to put into words. Hopefully there is a much easier solution, or at least someone can modify what I have started.
Update: renamed the array fasta_ids, as it is in the code (sorry if any confusion)
fasta_id = frag_id
Non optimized version. array.index(x) returns index of x in array or nil if not found. compact then removes nil elements from the array.
position_of_frag_id_in_d = frag_ids.map{|x| frag_by_density.index(x)}.compact

Ruby Exercise: Count the numbers in an array between a given range

So working through the above exercise and found this solution on GitHub.
def count_between arr, lower, upper
return 0 if arr.length == 0 || lower > upper
return arr.length if lower == upper
range = (lower..upper).to_a
arr.select { |value| range.include?(value) }.length
end
I understand what the first three lines mean and why they return the values they do. What I'd like to understand are the following lines of code.
Line 4 (below) is defining "range" as a variable and uses the lower...upper as the range variables (just discovered you don't need to put an integer value in a range. What does '.to_a' mean, can't seem to find it in the ruby docs, and what does it do?
range = (lower..upper).to_a
Line 5 (below) is using an Array#select method and its saying select this value if the value is included in this range and then give me the Array#length of all selected values, but I don't quite understand A. what |value| is doing and what it means. B. range.include?(value) means is this value included in this range I am assuming.
arr.select { |value| range.include?(value) }.length
Actually, I'd simplify to this:
def count_between arr, lower, upper
return 0 if lower > upper
arr.count{|v| (lower..upper).include?(v)}
end
to_a is documented here; it returns an Array containing each element in the Range. However, there's no reason to call to_a on the Range before calling include?.
There's also no reason to special-case the empty array.
Returning the length of the array when lower equals upper makes no sense.
value is the name given to the value the block is called with. I think a simple v is better for such a trivial case.
select calls the block for each value in arr and returns a new Array containing the elements for which the block returns true, so the length of that new Array is the number of matching values. However, count exists, and makes more sense to use, since the count is all we care about.
Update: As #steenslag points out in the comments, Comparable#between? can be used instead of creating a Range on which to call include?, and this eliminates the need to ensure that lower is less than or equal to upper:
def count_between arr, lower, upper
arr.count{|v| v.between?(lower, upper)}
end
to_a means convert to array
irb(main):001:0> (1..5).to_a
=> [1, 2, 3, 4, 5]
select method passes each element to the block and Returns a new array containing all elements of ary for which the given block returns a true value.. In your case it simply checks if the value is contained in the range array. range is an array not a range.
## if arr is [1,5] for eg:
irb(main):005:0> [1,5].select {|value| range.include?(value)}
=> [1, 5]
irb(main):006:0> [1,5].select {|value| range.include?(value)}.length
=> 2
so the elements of arr are contained in the |value| variable inside the block.
It's a block.
As the documentation says: select "Returns a new array containing all elements of ary for which the given block returns a true value."
So for each object in arr it is passed to the block in which you provide whatever code you want to that returns true or false, and the select statement uses this result to add the value to the the array that it returns. And after that, length is called on the array.
So you have an array, you filter the array to contain only the numbers that are in the range, and then you take the length - effectively counting the number of elements.

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

Resources