I am trying to see if an array contains each element of another array. Plus I want to account for the duplicates. For example:
array = [1, 2, 3, 3, "abc", "de", "f"]
array contains [1, 2, 3, 3] but does not contain [2, 2, "abc"] - too many 2's
I have tried the below but obviously doesn't take into account the dupes.
other_arrays.each { |i| array.include? i }
This method iterates once over both arrays.
For each array, it creates a hash with the number of occurences of each element.
It then checks that for every unique element in subset, there are at least as many elements in superset.
class Array
def count_by
each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end
def subset_of?(superset)
superset_counts = superset.count_by
count_by.all? { |k, count| superset_counts[k] >= count }
end
end
[1, 2, 3, 3, "abc", "de", "f"].count_by
#=> {1=>1, 2=>1, 3=>2, "abc"=>1, "de"=>1, "f"=>1}
[1, 2, 3, 3].count_by
#=> {1=>1, 2=>1, 3=>2}
[1, 2, 3, 3].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> true
[2, 2, "abc"].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> false
If you don't want to patch the Array class, you could define :
count_by(array) and subset_of?(array1, array2).
You could first create a useful instance method for the class Array:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Then all elements of an array a are contained in array b if the following method returns true.
def subarray?(a,b)
a.difference(b).empty?
end
For example,
subarray? [1,2,3], [1,4,"cat",3,2]
#=> true
subarray? [1,2,3], [1,4,"cat",3,5]
#=> false
I've found Array#difference has such wide application that I proposed that it be added to the Ruby core. Details about the method and its uses can be found at the link and also in my answer to this SO question.
Related
Input :
arr = [4,2,'b',5,'c','a',7]
Output:
[2,4,5,7,'a','b','c']
I can think of this :
int_arr = arr.select {|x| x.instance_of?(Integer)}.sort
str_arr = arr.select {|x| x.instance_of?(String)}.sort
int_arr + str_arr
Please suggest a efficient way to do it.
One way is to partition your array by those elements being Integer, to make sure they remain first and then sort the elements of each array:
[4,2,'b',5,'c','a',7].partition { |e| e.is_a?(Integer) }.flat_map(&:sort)
# [2, 4, 5, 7, "a", "b", "c"]
arr.sort_by { |e| [e.is_a?(Integer) ? 0 : 1, e] }
#=> [2, 4, 5, 7, "a", "b", "c"]
[22, 'efg', 0, 'abc', -4].sort_by { |e| [e.is_a?(Integer) ? 0 : 1, e] }
#=> [-4, 0, 22, "abc", "efg"]
See Enumerable#sort_by. sort_by performs pair-wise comparisons of 2-element arrays using the method Array#<=>. See especially the third paragraph of that doc.
The following could be used if, as in the example, the integers are single digits and the strings are single characters.
arr.sort_by(&:ord)
#=> [2, 4, 5, 7, "a", "b", "c"]
See Integer#ord and String#ord.
I'm using Ruby 1.9.3 and I want to remove values from an array that appear more than once. I have the following:
arr = [1,2,2,3,4,5,6,6,7,8,9]
and the result should be:
arr = [1,3,4,5,7,8,9].
What would be the simplest, shortest Ruby code to accomplish this?
As #Sergio Tulentsev mentioned combination of group_by and select will do the trick
Here you go
arr.group_by{|i| i}.select{|k, v| v.count.eql?(1)}.keys
We can achieve this by array select and count methods
arr.select { |x| arr.count(x) == 1 } #=> [1, 3, 4, 5, 7, 8, 9]
def find_duplicates(elements)
encountered = {}
# Examine all elements in the array.
elements.each do |e|
# If the element is in the hash, it is a duplicate.
if encountered[e]
#Remove the element
else
# Record that the element was encountered.
encountered[e] = 1
end
end
end
I want to remove values from an array that appear more than once.
to check element appear more than once use Array#count
to remove element conditionally use Array#delete_if
below is an example:
> arr.delete_if{|e| arr.count(e) > 1}
#=> [1, 3, 4, 5, 7, 8, 9]
Option2:
> arr.group_by{|e| e}.delete_if{|_,v| v.size > 1}.keys
#=> [1, 3, 4, 5, 7, 8, 9]
First of you need to group elements by itself (which will return key, value pair), then remove such elements which appear more than once(value), and use keys
I would be inclined to use a counting hash.
Code
def single_instances(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }.
select { |_,v| v == 1 }.
keys
end
Example
single_instances [1,2,2,3,4,5,6,6,7,8,9]
#=> [1, 3, 4, 5, 7, 8, 9]
Explanation
The steps are as follows.
arr = [1,2,2,3,4,5,6,6,7,8,9]
f = Hash.new(0)
#=> {}
f is created with the method Hash::new with an argument of zero. That means that if f does not have a key k, f[k] returns zero (and does not alter f).
enum = arr.each_with_object(f)
#=> #<Enumerator: [1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9]:each_with_object({})>
h = enum.each { |e,h| h[e] += 1 }
#=> {1=>1, 2=>2, 3=>1, 4=>1, 5=>1, 6=>2, 7=>1, 8=>1, 9=>1}
g = h.select { |_,v| v == 1 }
#=> {1=>1, 3=>1, 4=>1, 5=>1, 7=>1, 8=>1, 9=>1}
g.keys
#=> [1, 3, 4, 5, 7, 8, 9]
In calculating g, Hash#select (which returns a hash), not Enumerable#select (which returns an array), is executed. I've used an underscore for the first block variable (a key in h) to signify that it is not used in the block calculation.
Let's look more carefully at the calculation of h. The first value is generated by the enumerator enum and passed to the block, and the block variables are assigned values using a process called disambiguation or decomposition.
e, h = enum.next
#=> [1, {}]
e #=> 1
h #=> {}
so the block calculation is
h[e] += 1
#=> h[e] = h[e] + 1 => 0 + 1 => 1
h[e] on the right side of the equality (using the method Hash#[], as contrasted with Hash#[]= on the left side of the equality), returns 1 because h has no key e #=> 1.
The next two elements of enum are passed to the block and the following calculations are performed.
e, h = enum.next
#=> [2, {1=>1}]
h[e] += 1
#=> h[e] = h[2] + 1 => 0 + 1 => 1
Notice that h has been updated.
e, h = enum.next
#=> [2, {1=>1, 2=>1}]
h[e] += 1
#=> h[e] = h[e] + 1 => h[2] + 1 => 1 + 1 => 2
h #=> {1=>1, 2=>2}
This time, because h already has a key e #=> 2, the hash's default value is not used.
The remaining calculations are similar.
Use [Array#difference] instead
A simpler way is to use the method Array#difference.
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Suppose
arr = [1,2,2,3,4,2,5,6,6,7,8,9]
Note the addition of a third 2.
arr - arr.difference(arr.uniq)
# => [1, 3, 4, 5, 7, 8, 9]
The three steps are as follows.
a = arr.uniq
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = arr.difference(a)
#=> [2, 2, 6] (elements that appear more than once)
arr - b
# => [1, 3, 4, 5, 7, 8, 9]
I've proposed that Array#diffence be added to the Ruby core, but there seems to be little interest in doing so.
I got this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
I want to sum the numeric values, so I have converted this into a single array by using flatten:
array = array.flatten
#=> ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
then:
a = []
array.each do |r|
a << r if r.class == Fixnum
end
a
This works, but I am searching for a simpler solution, maybe converting the array into an "only numeric" array first:
[20, 3, 3, 20]
Not that obvious, but you can pass a class or module to grep to select its instances:
array.grep(Integer)
#=> [20, 3, 3, 20]
or
array.grep(Numeric)
#=> [20, 3, 3, 20]
or to grep_v in order to exclude its instances:
array.grep_v(String)
#=> [20, 3, 3, 20]
This works because grep uses === for pattern matching. If you pass a class, it invokes Module#=== (each class is a module) which returns true if the object is an instance of the receiver:
Integer === 20
#=> true
Integer === 'foo'
#=> false
However, your actual problem can be solved much easier. Given this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
And assuming that the second value is always a number, you can use sum with a block:
array.sum { |string, number| number }
#=> 46
or to sum each sub-array's last value: (which is the same in this case)
array.sum(&:last)
#=> 46
One might use class’ case-equality Module#=== to determine a class of elements and to reject strings:
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.reject &String.method(:===)
#⇒ [
# [0] 20,
# [1] 3,
# [2] 3,
# [3] 20
# ]
array.delete_if { |x| x.kind_of(String) }
array.map { |item| item if item.is_a? Integer }.compact
# [20, 3, 3, 20]
More compact:
array.select { |item| item.is_a? Integer }
Or even shorter:
array.select &Integer.method(:===)
Follow the below code
array.reject { |c| c.class.name.eql?('String') }
With is_a?
array.reject { |c| c.is_a?(String) }
If your arrays are formatted like your example then this will work:
array.select.with_index { |_,i| i.odd? } #=> [20, 3, 3, 20]
use select to select only integers from the array
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.select { |tmp| tmp.is_a?(Integer) }
#=> [20, 3, 3, 20]
use select! if you want to permanently change the content of your variable array with the integer-only content
The goal is to find if the letter 'a' is within a string and add the three positions away from the 'a' occurrences within the string.
So if the string was 'gabe' my list would look like this list = [2,3,4].
If the string is 'gabba' then list = [2,3,4,5,6,7]
my current code seems to be giving me errors
def nearby_az(string)
list = []
for i in 0..(string.length)
if string[i] == 'a'
list.push(i+1)
list.push(i+2)
list.push(i+3)
next
end
return list
end
I get the following error:
(repl):11: syntax error, unexpected end-of-input, expecting keyword_end
can you see where my logic falls off?
The error comes from the fact you haven't closed the range block with an end. But there are other points. I suggest you to try something like this:
def nearby_az(str)
list = Array.new
pos = -1
str.each_char do |c|
pos = pos + 1
if (c == 'a') then
list.push(pos+1)
list.push(pos+2)
list.push(pos+3)
end
end
list
end
or yet better
def nearby_az(str)
list = Array.new
nstr = str.each_char.to_a
nstr.each_index do |i|
if (nstr[i] == 'a') then
list.push(i+1)
list.push(i+2)
list.push(i+3)
end
end
list
end
(This ways you don't even have to create an artificial index, by using the natural index of the nstr array)
With this code, if you do
puts nearby_az("asdfgaqwer")
the result will be [ 1, 2, 3, 6, 7, 8], as desired.
Remember you don't need a return in Ruby. The value of the last expression calculated in a method is returned by default to the method caller.
Of course you may continue using your way, doing this:
def nearby_az(string)
list = []
for i in 0..(string.length)
if string[i] == 'a'
list.push(i+1)
list.push(i+2)
list.push(i+3)
end
end
list
end
And it will give you the same result, although I think the first code is a bit easier to read.
Here is a more Ruby-like way you could do that.
Code
def indices(str)
str.each_char.
with_index.
select { |c,_| c=='a' }.
flat_map { |_,i| (i..i+2).to_a }
end
Examples
indices "gabe"
#=> [1, 2, 3]
indices "gabba"
#=> [1, 2, 3, 4, 5, 6]
indices "abbadabbadoo"
#=> [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
Explanation
Suppose
str = "gagga"
Then the steps are as follows:
enum0 = str.each_char
#=> #<Enumerator: "gagga":each_char>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: "gagga":each_char>:with_index>
Carefully examine the above return value. You can think of enum1 as a compound enumerator (though Ruby has no such concept--enum1 is simply an enumerator). We can see the elements of enum1 that will be passed to select by converting enum1 to an array:
enum1.to_a
#=> [["g", 0], ["a", 1], ["g", 2], ["g", 3], ["a", 4]]
Continuing,
a = enum1.select { |c,_| c=='a' }
#=> [["a", 1], ["a", 4]]
a.flat_map { |e,i| (i..i+2).to_a }
#=> [1, 2, 3, 4, 5, 6]
Enumerable#flat_map's block variables are e and i. When the first element of a (["a", 1]) is passed to the block, the block variables are assigned using parallel assignment:
e, i = ["a", 1]
#=> ["a", 1]
e #=> "a"
i #=> 1
and the block calculation is performed:
(i..i+2).to_a
#=> (1..3).to_a
#=> [1,2,3]
Note that flat_map is equivalent to:
b = a.map { |e,i| (i..i+2).to_a }
#=> [[1, 2, 3], [4, 5, 6]]
c = b.flatten(1)
#=> [1, 2, 3, 4, 5, 6]
One last thing: flat_map's first block variable, e, is not used in the block calculation. As is common in such situations, _ (a legitimate local variable) is used instead for that variable. That informs the reader that that block variable is not used, and may also reduce the chances of introducing errors within the block.
I added my own method to the Array class that does the same thing as Array#uniq.
This is my version:
arr = ["fun", "sun", 3, 5, 5, 5, 1, 2, 1, "fun"]
class Array
def my_uniq
new_arr = []
each do |item|
new_arr << item unless new_arr.include?(item)
end
new_arr
end
end
print arr.my_uniq
Is there a way to modify this to return the indices of the unique elements rather than the elements themselves?
each_with_index will allow you to iterate your array and return indexes.
each_with_index do |item, index|
newArr << index unless newArr.include?(item)
end
class Array
def indices_uniq
uniq.map { |e| index(e) }
end
end
arr = ["fun", "sun", 3, 5, 5, 5, 1, 2, 1, "fun"]
arr.indices_uniq
#=> [0, 1, 2, 3, 6, 7]
To see what's going on here, let's write this more verbosely and include some code to display intermediate values:
class Array
def indices_uniq
puts "self = #{self}"
arr = self
u = arr.uniq
puts "u = #{u}"
u.map { |e|
puts "#{e} is at index #{index(e)}"
arr.index(e) }
end
end
arr.indices_uniq
# self = ["fun", "sun", 3, 5, 5, 5, 1, 2, 1, "fun"]
# u = ["fun", "sun", 3, 5, 1, 2]
# fun is at index 0
# sun is at index 1
# 3 is at index 2
# 5 is at index 3
# 1 is at index 6
# 2 is at index 7
#=> [0, 1, 2, 3, 6, 7]
We can substitute out u and arr:
class Array
def indices_uniq
self.uniq.map { |e| self.index(e) }
end
end
arr.indices_uniq
#=> [0, 1, 2, 3, 6, 7]
The key: self is the receiver for methods that do not have explicit receivers. In the last version of the methods uniq and include both have the explicit receiver self. It follows that if the explicit receiver is removed, the receiver will still be self:
class Array
def indices_uniq
uniq.map { |e| index(e) }
end
end
arr.indices_uniq
#=> [0, 1, 2, 3, 6, 7]
Another way of doing this is to change the operative line to:
map { |e| index(e) }.uniq