Ruby, Delete elements (slices) from a multidimensional array - arrays

(Pretty new with Ruby)
I can remove a block of elements from a single-dimensional array
array1D = Array.new(6){|i| i*i}
array1D.slice!(2,2) #=> [1, 16, 25]
len = array1D.length #=> 4
However,
Array(arrayd3d[0][0]).slice!(30000,8880)
on arrayd3d[1][1][38884],
I still get
len = array3D.length #=> 38884
1) What I'm doing wrong?
2) How can I delete the same block of elements (30000,8880) from all
arrayd3d[1..nDim1][1..nDim2]?

slice! returns the deleted object:
a = [ "a", "b", "c" ]
a.slice!(1) #=> "b"
a #=> ["a", "c"]
In general in ruby we prefer not to alter the original object unless we're looking for some particular performance gain (very very rare, eg maybe you want to reduce the memory consumption of a very large array before moving on).
That's the reason for the exclamation symbol (! aka bang) which usually indicates some destructive behaviour.
Please consider using the non-bang version instead.
Array.new(6){ |i| i*i }
y = array1D.slice(2,2)
or
def some_method(input_array)
input_array.slice(2,2)
end
x = Array.new(6){ |i| i*i }
y = some_method(x)
This way your code becomes more predictable as you're not altering the value of your arguments.

Related

How do I find the index of any element from an array within another array?

I have an array:
["a", "b", "c", "d"]
How do I figure out the index of the first element of the above array to occur within a second array:
["next", "last", "d", "hello", "a"]
The index of the first element from the first array to occur within the above array would be 2; "d" belongs to the first array and occurs at position 2.
There's a couple of ways to do this, but the naive approach might work well enough to get going:
tests = ["a", "b", "c", "d"]
in_array = ["next", "last", "d", "hello", "a"]
in_array.each_with_index.find do |e, i|
tests.include?(e)
end
# => ["d", 2]
You can speed this up by making tests a Set which avoids a lot of O(N) lookups:
tests = Set.new([ ... ])
The same code will work with include? but that's now much faster on longer lists.
This approach, wrapped in a method, returns an array containing all indexes of common elements between two arrays.
def find_positions(original_array, look_up_array)
positions_array = []
original_array.each do |x|
if look_up_array.index(x) != nil
positions_array << look_up_array.index(x)
end
end
positions_array
# positions_array.first => for the first matched element
end
If you want only the first matched element you could return positions_array.first but this way you'll not avoid the extra lookups.
PS: you could also use #collect and avoid the extra array (positions_array)
You can iterate through the array you want to be compared and use .select or .find iterator method. .find will select the first element match in the arrays while .select will match all elements in the arrays. If you want to add the index in the selection you can add .each_with_index. '.index(a)' returns the element if present else it will return nil.
alphabet = %w(a b c d)
%w(next last d hello a).each_with_index.find {|a, _index| alphabet.index(a) }
=> ["d", 2]
%w(next last d hello a).each_with_index.select {|a, _index| alphabet.index(a) }[0]
=> ["d", 2]
# if you just need the index of the first match
%w(next last d hello a).index {|a| alphabet.index(a) }
=> 2

Ruby : Choosing between each, map, inject, each_with_index and each_with_object

When I started writing Ruby many years ago, it took me a while to understand the difference between each and map. It only got worse when I discovered all the other Enumerable and Array methods.
With the help of the official documentation and many StackOverflow questions, I slowly began to understand what those methods did.
Here is what took me even longer to understand though :
Why should I use one method or another?
Are there any guidelines?
I hope this question isn't a duplicate : I'm more interested in the "Why?" than the "What?" or "How?", and I think it could help Ruby newcomers.
A more tl;dr answer:
How to choose between each, map, inject, each_with_index and each_with_object?
Use #each when you want "generic" iteration and don't care about the result. Example - you have numbers, you want to print the absolute value of each individual number:
numbers.each { |number| puts number.abs }
Use #map when you want a new list, where each element is somehow formed by transforming the original elements. Example - you have numbers, you want to get their squares:
numbers.map { |number| number ** 2 }
Use #inject when you want to somehow reduce the entire list to one single value. Example - you have numbers, you want to get their sum:
numbers.inject(&:+)
Use #each_with_index in the same situation as #each, except you also want the index with each element:
numbers.each_with_index { |number, index| puts "Number #{number} is on #{index} position" }
Uses for #each_with_object are more limited. The most common case is if you need something similar to #inject, but want a new collection (as opposed to singular value), which is not a direct mapping of the original. Example - number histogram (frequencies):
numbers.each_with_object({}) { |number, histogram| histogram[number] = histogram[number].to_i.next }
Which object can I use?
First, the object you're working with should be an Array, a Hash, a Set, a Range or any other object that respond to each. If it doesn't, it might be converted to something that will. You cannot call each directly on a String for example, because you need to specify if you'd like to iterate over each byte, character or line.
"Hello World".respond_to?(:each)
#=> false
"Hello World".each_char.respond_to?(:each)
#=> true
I want to calculate something with each element, just like with a for loop in C or Java.
If you want to iterate over each element, do something with it and not modify the original object, you can use each. Please keep reading though, in order to know if you really should.
array = [1,2,3]
#NOTE: i is a bound variable, it could be replaced by anything else (x, n, element). It's a good idea to use a descriptive name if you can
array.each do |i|
puts "La"*i
end
#=> La
# LaLa
# LaLaLa
It is the most generic iteration method, and you could write any of the other mentioned methods with it. We will actually, for pedagogical purposes only. If you spot a similar pattern in your code, you could probably replace it with the corresponding method.
It is basically never wrong to use each, it is almost never the best choice though. It is verbose and not Ruby-ish.
Note that each returns the original object, but this is rarely (never?) used. The logic happens inside the block, and should not modify the original object.
The only time I use each is:
when no other method would do. The more I learn about Ruby, the less often it happens.
when I write a script for someone who doesn't know Ruby, has some programming experience (e.g. C, Fortran, VBA) and would like to understand my code.
I want to get an Array out of my String/Hash/Set/File/Range/ActiveRecord::Relation
Just call object.to_a.
(1..10).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"Hello world".each_char.to_a
#=> ["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
{:a => 1, :b => 2}.to_a
#=> [[:a, 1], [:b, 2]]
Movie.all.to_a #NOTE: Probably very inefficient. Try to keep an ActiveRecord::Relation as Relation for as long as possible.
#=> [Citizen Kane, Trois couleurs: Rouge, The Grapes of Wrath, ....
Some methods described below (e.g. compact, uniq) are only defined for Arrays.
I want to get a modified Array based on the original object.
If you want to get an Array based on the original object, you can use map. The returned object will have the same size as the original one.
array = [1,2,3]
new_array = array.map do |i|
i**2
end
new_array
#=> [1, 4, 9]
#NOTE: map is often used in conjunction with other methods. Here is the corresponding one-liner, without creating a new variable :
array.map{|i| i**2}
#=> [1, 4, 9]
# EACH-equivalent (For pedagogical purposes only):
new_array = []
array.each do |i|
new_array << i**2
end
new_array
#=> [1, 4, 9]
The returned Array will not replace the original object.
This method is very widely used. It should be the first one you learn after each.
collect is a synonym of map. Make sure to use only one of both in your projects.
I want to get a modified Hash based on the original Hash.
If your original object is a Hash, map will return an Array anyway. If you want a Hash back :
hash = {a: 1, b: 2}
hash.map{|key, value| [key, value*2]}.to_h
#=> {:a=>2, :b=>4}
# EACH-equivalent
hash = {a: 1, b: 2}
new_hash = {}
hash.each do |key,value|
new_hash[key]=value*2
end
new_hash
#=> {:a=>2, :b=>4}
I want to filter some elements.
I want to remove nil elements
You can call compact. It will return a new Array without the nil elements.
array = [1,2,nil,4,5]
#NOTE: array.map{|i| i*2} Would raise a NoMethodError
array.compact
# => [1, 2, 4, 5]
# EACH-equivalent
new_array = []
array.each do |integer_or_nil|
new_array << integer_or_nil unless integer_or_nil.nil?
end
new_array
I want to write some logic to determine if an element should be kept in the new Array
You can use select or reject.
integers = (1..10)
integers.select{|i| i.even?}
# => [2, 4, 6, 8, 10]
integers.reject{|i| i.odd?}
# => [2, 4, 6, 8, 10]
# EACH-equivalent
new_array = []
integers.each do |i|
new_array << i if i.even?
end
new_array
I want to remove duplicate elements from your Array
You can use uniq :
letters = %w(a b a b c)
letters.uniq
#=> ["a", "b", "c"]
# EACH-equivalent
uniq_letters = []
letters.each do |letter|
uniq_letters << letter unless uniq_letters.include?(letter)
end
uniq_letters
#TODO: Add find/detect/any?/all?/count
#TODO: Add group_by/sort/sort_by
I want to iterate over all the elements while counting from 0 to n-1
You can use each_with_index :
letters = %w(a b c)
letters.each_with_index do |letter, i|
puts "Letter ##{i} : #{letter}"
end
#=> Letter #0 : a
# Letter #1 : b
# Letter #2 : c
#NOTE: There's a nice Ruby syntax if you want to use each_with_index with a Hash
hash = {:a=>1, :b=>2}
hash.each_with_index{|(key,value),i| puts "#{i} : #{key}->#{value}"}
# => 0 : a->1
# 1 : b->2
# EACH-equivalent
i = 0
letters.each do |letter|
puts "Letter ##{i} : #{letter}"
i+=1
end
each_with_index returns the original object.
I want to iterate over all the elements while setting a variable during each iteration and using it in the next iteration.
You can use inject :
gauss = (1..100)
gauss.inject{|sum, i| sum+i}
#=> 5050
#NOTE: You can specify a starting value with gauss.inject(0){|sum, i| sum+i}
# EACH-equivalent
sum = 0
gauss.each do |i|
sum = sum + i
end
puts sum
It returns the variable as defined by the last iteration.
reduce is a synonym. As with map/collect, choose one keyword and keep it.
I want to iterate over all the elements while keeping a variable available to each iteration.
You can use each_with_object :
letter_ids = (1..26)
letter_ids.each_with_object({}){|i,alphabet| alphabet[("a".ord+i-1).chr]=i}
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
# EACH-equivalent
alphabet = {}
letter_ids.each do |i|
letter = ("a".ord+i-1).chr
alphabet[letter]=i
end
alphabet
It returns the variable as modified by the last iteration. Note that the order of the two block variables is reversed compared to inject.
If your variable is a Hash, you should probably prefer this method to inject, because h["a"]=1 returns 1, and it would require one more line in your inject block to return a Hash.
I want something that hasn't been mentioned yet.
Then it's probably okay to use each ;)
Notes :
It's a work in progress, and I would gladly appreciate any feedback. If it's interesting enough and fit in one page, I might extract a flowchart out of it.

Most efficient way to count duplicated elements between two arrays

As part of a very basic program I am writing in Ruby, I am trying to find the total number of shared elements between two arrays of equal length, but
I need to include repeats.
My current example code for this situation is as follows:
array_a = ["B","A","A","A","B"]
array_b = ["A","B","A","B","B"]
counter = 0
array_a.each_index do |i|
array_a.sort[i] == array_b.sort[i]
counter += 1
end
end
puts counter
I want the return value of this comparison in this instance to be 4, and not 2, as the two arrays share 2 duplicate characters ("A" twice, and "B" twice). This seems to work, but I am wondering if there are any more efficient solutions for this issue. Specifically whether there are any methods you would suggest looking into. I spoke with someone who suggested a different method, inject, but I really don't understand how that applies and would like to understand. I did quite a bit of reading on uses for it, and it still isn't clear to me how it is appropriate. Thank you.
Looking at my code, I have realized that it doesn't seem to work for the situation that I am describing.
Allow me to reiterate and explain what I think the OP's original intent was:
Given arrays of equal size
array_a = ["B","A","A","A","B"]
array_b = ["A","B","A","B","B"]
We need to show the total number of matching pairs of elements between the two arrays. In other words, each B in array_a will "use up" a B in array_b, and the same will be true for each A. As there are two B's in array_a and three in array_b, this leaves us with a count of 2 for B, and following the same logic, 2 for A, for a sum of 4.
(array_a & array_b).map { |e| [array_a.count(e), array_b.count(e)].min }.reduce(:+)
If we get the intersection of the arrays with &, the result is a list of values that exist in both arrays. We then iterate over each match, and select the minimum number of times the element exists in either array --- this is the most number of times the element that can be "used". All that is left is to total the number of paired elements, with reduce(:+)
Changing array_a to ["B", "A", "A", "B", "B"] results in a total of 5, as there are now enough of B to exhaust the supply of B in array_b.
If I understand the question correctly, you could do the following.
Code
def count_shared(arr1, arr2)
arr1.group_by(&:itself).
merge(arr2.group_by(&:itself)) { |_,ov,nv| [ov.size, nv.size].min }.
values.
reduce(0) { |t,o| (o.is_a? Array) ? t : t + o }
end
Examples
arr1 = ["B","A","A","A","B"]
arr2 = ["A","B","A","B","B"]
count_shared(arr1, arr2)
#=> 4 (2 A's + 2 B's)
arr1 = ["B", "A", "C", "C", "A", "A", "B", "D", "E", "A"]
arr2 = ["C", "D", "F", "F", "A", "B", "A", "B", "B", "G"]
count_shared(arr1, arr2)
#=> 6 (2 A's + 2 B's + 1 C + 1 D + 0 E's + 0 F's + 0 G's)
Explanation
The steps are as follows for a slightly modified version of the first example.
arr1 = ["B","A","A","A","B","C","C"]
arr2 = ["A","B","A","B","B","D"]
First apply Enumerable#group_by to both arr1 and arr2:
h0 = arr1.group_by(&:itself)
#=> {"B"=>["B", "B"], "A"=>["A", "A", "A"], "C"=>["C", "C"]}
h1 = arr2.group_by(&:itself)
#=> {"A"=>["A", "A"], "B"=>["B", "B", "B"], "D"=>["D"]}
Prior to Ruby v.2.2, when Object#itself was introduced, you would have to write:
arr.group_by { |e| e }
Continuing,
h2 = h0.merge(h1) { |_,ov,nv| [ov.size, nv.size].min }
#=> {"B"=>2, "A"=>2, "C"=>["C", "C"], "D"=>["D"]}
I will return shortly to explain the above calculation.
a = h2.values
#=> [2, 2, ["C", "C"], ["D"]]
a.reduce(0) { |t,o| (o.is_a? Array) ? t : t + o }
#=> 4
Here Enumerable#reduce (aka inject) merely sums the values of a that are not arrays. The arrays correspond to elements of arr1 that do not appear in arr2 or vise-versa.
As promised, I will now explain how h2 is computed. I've used the form of Hash#merge that employs a block (here { |k,ov,nv| [ov.size, nv.size].min }) to compute the values of keys that are present in both hashes being merged. For example, when the first key-value pair of h1 ("A"=>["A", "A"]) is being merged into h0, since h0 also has a key "A", the array
["A", ["A", "A", "A"], ["A", "A"]]
is passed to the block and the three block variables are assigned values (using "parallel assignment", which is sometimes called "multiple assignment"):
k, ov, nv = ["A", ["A", "A", "A"], ["A", "A"]]
so we have
k #=> "A"
ov #=> ["A", "A", "A"]
nv #=> ["A", "A"]
k is the key, ov ("old value") is the value of "A" in h0 and nv ("new value") is the value of "A" in h1. The block calculation is
[ov.size, nv.size].min
#=> [3,2].min = 2
so the value of "A" is now 2.
Notice that the key, k, is not used in the block calculation (which is very common when using this form of merge). For that reason I've changed the block variable from k to _ (a legitimate local variable), both to reduce the chance of introducing a bug and to signal to the reader that the key is not used in the block. The other elements of h2 that use this block are computed similarly.
Another way
It would be quite simple if we had available an Array method I've proposed be added to the Ruby core:
array_a = ["B","A","A","A","B"]
array_b = ["A","B","A","B","B"]
array_a.size - (array_a.difference(array_b)).size
#=> 4
or
array_a.size - (array_b.difference(array_a)).size
#=> 4
I've cited other applications in my answer here.
This is a perfect job for Enumerable#zip and Enumerable#count:
array_a.zip(array_b).count do |a, b|
a == b
end
# => 2
The zip method pairs up elements, "zippering" them together, and the count method can take a block as to if the element should be counted.
The inject method is very powerful, but it's also the most low-level. Pretty much every other Enumerable method can be created with inject if you work at it, so it's quite flexible, but usually a more special-purpose method is better suited. It's still a useful tool if applied correctly.
In this case zip and count do a much better job and if you know what these methods do, this code is self explanatory.
Update:
If you need to count all overlapping letters regardless of order you need to do some grouping on them. Ruby on Rails provides the handy group_by method in ActiveSupport, but in pure Ruby you need to make your own.
Here's an approach that counts up all the unique letters, grouping them using chunk:
# Convert each array into a map like { "A" => 2, "B" => 3 }
# with a default count of 0.
counts = [ array_a, array_b ].collect do |a|
Hash.new(0).merge(
Hash[a.sort.chunk { |v| v }.collect { |k, a| [ k, a.length ] }]
)
end
# Iterate over one of the maps key by key and count the minimum
# overlap between the two.
counts[0].keys.inject(0) do |sum, key|
sum + [ counts[0][key], counts[1][key] ].min
end

Ruby: Find index of next match in array, or find with offset

I want to find further matches after Array#find_index { |item| block } matches for the first time. How can I search for the index of the second match, third match, and so on?
In other words, I want the equivalent of the pos argument to Regexp#match(str, pos) for Array#find_index. Then I can maintain a current-position index to continue the search.
I cannot use Enumerable#find_all because I might modify the array between calls (in which case, I will also adjust my current-position index to reflect the modifications). I do not want to copy part of the array, as that would increase the computational complexity of my algorithm. I want to do this without copying the array:
new_pos = pos + array[pos..-1].find_index do |elem|
elem.matches_condition?
end
The following are different questions. They only ask the first match in the array, plus one:
https://stackoverflow.com/questions/11300886/ruby-how-to-find-the-next-match-in-an-array
https://stackoverflow.com/questions/4596517/ruby-find-next-in-array
The following question is closer, but still does not help me, because I need to process the first match before continuing to the next (and this way also conflicts with modification):
https://stackoverflow.com/questions/9925654/ruby-find-in-array-with-offset
A simpler way to do it is just:
new_pos = pos
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
end
new_pos = nil if new_pos == array.size
In fact, I think this is probably better than my other answer, because it's harder to get wrong, and there's no chance of future shadowing problems being introduced from the surrounding code. However, it's still clumsy.
And if the condition is more complex, then you end up needing to do something like this:
new_pos = pos
# this check is only necessary if pos may be == array.size
if new_pos < array.size
prepare_for_condition
end
while new_pos < array.size and not array[new_pos].matches_condition?
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end
new_pos = nil if new_pos == array.size
Or, God forbid, a begin ... end while loop (although then you run into trouble with the initial value of new_pos):
new_pos = pos - 1
begin
new_pos += 1
if new_pos < array.size
prepare_for_condition
end
end while new_pos < array.size and not array[new_pos].matches_condition?
new_pos = nil if new_pos == array.size
This may seem horrible. However, supposing prepare_for_condition is something that keeps being tweaked in small ways. Those tweaks will eventually get refactored; however, by that time, the output of the refactored code will also end up getting tweaked in small ways that don't belong with the old refactored code, but do not yet seem to justify refactoring of their own - and so on. Occasionally, someone will forget to change both places. This may seem pathological; however, in programming, as we all know, the pathological case has a habit of occurring only too often.
Here is one way this can be done. We can define a new method in Array class that will allow us to find indexes that match a given condition. The condition can be specified as block that returns boolean.
The new method returns an Enumerator so that we get the benefit of many of the Enumerator methods such next, to_a, etc.
ary = [1,2,3,4,5,6]
class Array
def find_index_r(&block)
Enumerator.new do |yielder|
self.each_with_index{|i, j| yielder.yield j if block.call(i)}
end
end
end
e = ary.find_index_r { |r| r % 2 == 0 }
p e.to_a #=> [1, 3, 5]
p e.next
#=> 1
p e.next
#=> 3
ary[2]=10
p ary
#=> [1, 2, 10, 4, 5, 6]
p e.next
#=> 5
e.rewind
p e.next
#=> 1
p e.next
#=> 2
Note: I added a new method in Array class for demonstration purpose. Solution can be adapted easily to work without the monkey-patching
Of course, one way to do it would be:
new_pos = pos + (pos...array.size).find_index do |index|
elem = array[index]
elem.matches_condition?
end
However, this is clumsy and easy to get wrong. For example, you may forget to add pos. Also, you have to make sure elem isn't shadowing something. Both of these can lead to hard-to-trace bugs.
I find it hard to believe that an index argument to Array#find_index and Array#index still hasn't made it into the language. However, I notice Regexp#match(str,pos) wasn't there until version 1.9, which is equally surprising.
Suppose
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
and for each element n in findees we want the index of the first unmatched element m of arr for which proc[n] == m. For example, if n=3, then proc[3] #==> 9, so the first matching index in arr would be 0. For the next n=3 in findees, the first unmatched match in arr is at index 4.
We can do this like so:
arr = [9,1,4,1,9,36,25]
findees = [1,6,3,6,3,7]
proc = ->(n) { n**2 }
h = arr.each_with_index.with_object(Hash.new { |h,k| h[k] = [] }) { |(n,i),h| h[n] << i }
#=> {9=>[0, 4], 1=>[1, 3], 4=>[2], 36=>[5], 25=>[6]}
findees.each_with_object([]) { |n,a| v=h[proc[n]]; a << v.shift if v }
#=> [1, 5, 0, nil, 4, nil]
We can generalize this into a handy Array method as follow:
class Array
def find_indices(*args)
h = each_with_index.with_object(Hash.new {|h,k| h[k] = []}) { |(n,i),h| h[n] << i }
args.each_with_object([]) { |n,a| v=h[yield n]; a << v.shift if v }
end
end
arr.find_indices(*findees) { |n| n**2 }
#=> [1, 5, 0, nil, 4, nil]
arr = [3,1,2,1,3,6,5]
findees = [1,6,3,6,3,7]
arr.find_indices(*findees, &:itself)
#=> [1, 5, 0, nil, 4, nil]
My approach is not much different from the others but perhaps packaged cleaner to be syntactically similar to Array#find_index . Here's the compact form.
def find_next_index(a,prior=nil)
(((prior||-1)+1)...a.length).find{|i| yield a[i]}
end
Here's a simple test case.
test_arr = %w(aa ab ac ad)
puts find_next_index(test_arr){|v| v.include?('a')}
puts find_next_index(test_arr,1){|v| v.include?('a')}
puts find_next_index(test_arr,3){|v| v.include?('a')}
# evaluates to:
# 0
# 2
# nil
And of course, with a slight rewrite you could monkey-patch it into the Array class

How to find duplicates in array without using `uniq` method

I am doing a challenge to make a method that finds duplicate values in an array, and prints out a new array without the duplicates. Ruby has a built in uniq method; however, I am not allowed to use it.
In my mind, this should work:
def uniques(array)
tempPos = 0
arrayPos = 0
duplicate = true
result = [] # array the result will be "pushed" too
for arrayPos in 0..array.length
for tempPos in 0..array.length
# If the values at the indexes are the same. But the indexes are not the same.
# we have a duplicate
if array[arrayPos] == array[tempPos] && arrayPos != tempPos
duplicate = true
else
duplicate = false
end
if duplicate == false
result[arrayPos] = array[arrayPos]
end
end
puts duplicate
end
puts result.inspect
end
Output:
uniq *this is the short hand user input to run the method*
false
false
false
false
false
false
[1, 2, 1, 4, 5, nil]
I must be doing something wrong.
Are you allowed to use a Set?
require 'set'
array = [1, 2, 3, 3, 3, 4]
Set.new(array).to_a
#=> [1, 2, 3, 4]
An other way is to iterate over every pair in the array:
array.each_cons(2).with_object([array.first]) do |pair, result|
result << pair.last unless pair.first == pair.last
end
#=> [1, 2, 3, 4]
There are many ways to do that. Here's another. Suppose:
arr = [3,5,1,3,4,1,1]
Construct:
h = arr.group_by(&:itself)
#=> {3=>[3, 3], 5=>[5], 1=>[1, 1, 1], 4=>[4]}
The duplicates are given by:
h.select { |_,v| v.size > 1 }.keys
#=> [3, 1]
and an array without the duplicates is given by:
h.keys
#=> [3, 5, 1, 4]
Your logic works fine altough as mentioned above a set would work better. You could also sort the elements, and then find adjacent pairs that are the same value which wouldn't work as well as a set, but would have slightly better run-time than your current solution:
To polish what you currently have:
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = false
for tempPos in 0...result.length
# if the values at the indexes are the same... but the indexes are not the same...
# we have a duplicate
duplicate ||= (array[arrayPos] == result[tempPos])
end
if !duplicate
result << array[arrayPos]
end
end
puts result
end
an slightly better approach (altought still poor performance):
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = result.include?(array[arrayPos])
if !duplicate
result << array[arrayPos]
end
end
puts result
end
Although this solution is OK for a learning assignment, you should note that the complexity of this is O(n^2) (n-squared). What that means is that for an array of size n (for example n=10), you are doing n-squared (100) iterations.
It gets exponentially worse. If you have an array of length 1,000,000, you are doing 1,000,000,000,000 iterations. This is why using a set is so important, it's average run-time will be much lower.
A fairly simple way to so this is to leverage array.include?
new = []
arr.each { |x| new << x unless new.include?(x)}
puts new
That will give you an array (new) that only includes unique elements from the original array (arr)
Duplicate array easy way
arr1 = [1,3,4,5,6,6,6,1]
arry = Array.new(arr1)
puts arry
Find uniq array easy way using OR operator
arr1 = [1,3,4,5,6,6,6,1]
arr2 = Array.new # creating new array
arry = arr1 | arr2 # compare two array using OR operator
puts arry

Resources