Let's say I am trying to remove elements from array a = [1,1,1,2,2,3]. If I perform the following:
b = a - [1,3]
Then I will get:
b = [2,2]
However, I want the result to be
b = [1,1,2,2]
i.e. I only remove one instance of each element in the subtracted vector not all cases. Is there a simple way in Ruby to do this?
You may do:
a= [1,1,1,2,2,3]
delete_list = [1,3]
delete_list.each do |del|
a.delete_at(a.index(del))
end
result : [1, 1, 2, 2]
[1,3].inject([1,1,1,2,2,3]) do |memo,element|
memo.tap do |memo|
i = memo.find_index(e)
memo.delete_at(i) if i
end
end
Not very simple but:
a = [1,1,1,2,2,3]
b = a.group_by {|n| n}.each {|k,v| v.pop [1,3].count(k)}.values.flatten
=> [1, 1, 2, 2]
Also handles the case for multiples in the 'subtrahend':
a = [1,1,1,2,2,3]
b = a.group_by {|n| n}.each {|k,v| v.pop [1,1,3].count(k)}.values.flatten
=> [1, 2, 2]
EDIT: this is more an enhancement combining Norm212 and my answer to make a "functional" solution.
b = [1,1,3].each.with_object( a ) { |del| a.delete_at( a.index( del ) ) }
Put it in a lambda if needed:
subtract = lambda do |minuend, subtrahend|
subtrahend.each.with_object( minuend ) { |del| minuend.delete_at( minuend.index( del ) ) }
end
then:
subtract.call a, [1,1,3]
A simple solution I frequently use:
arr = ['remove me',3,4,2,45]
arr[1..-1]
=> [3,4,2,45]
a = [1,1,1,2,2,3]
a.slice!(0) # remove first index
a.slice!(-1) # remove last index
# a = [1,1,2,2] as desired
For speed, I would do the following, which requires only one pass through each of the two arrays. This method preserves order. I will first present code that does not mutate the original array, then show how it can be easily modified to mutate.
arr = [1,1,1,2,2,3,1]
removals = [1,3,1]
h = removals.group_by(&:itself).transform_values(&:size)
#=> {1=>2, 3=>1}
arr.each_with_object([]) { |n,a|
h.key?(n) && h[n] > 0 ? (h[n] -= 1) : a << n }
#=> [1, 2, 2, 1]
arr
#=> [1, 1, 1, 2, 2, 3, 1]
To mutate arr write:
h = removals.group_by(&:itself).transform_values(&:count)
arr.replace(arr.each_with_object([]) { |n,a|
h.key?(n) && h[n] > 0 ? (h[n] -= 1) : a << n })
#=> [1, 2, 2, 1]
arr
#=> [1, 2, 2, 1]
This uses the 21st century method Hash#transform_values (new in MRI v2.4), but one could instead write:
h = Hash[removals.group_by(&:itself).map { |k,v| [k,v.size] }]
or
h = removals.each_with_object(Hash.new(0)) { | n,h| h[n] += 1 }
Related
There is an array with some numbers. All numbers are equal except for one. I'm trying to get this type of thing:
find_uniq([ 1, 1, 1, 2, 1, 1 ]) == 2
find_uniq([ 0, 0, 0.55, 0, 0 ]) == 0.55
I tried this:
def find_uniq(arr)
arr.uniq.each{|e| arr.count(e)}
end
It gives me the two different values in the array, but I'm not sure how to pick the one that's unique. Can I use some sort of count or not? Thanks!
This worked:
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] != arr[1]
return arr[1] == arr[2] ? arr[0] : arr[1]
end
arr.each_cons(2) { |x, y| return y if y != x }
end
Thanks pjs and Cary Swoveland.
I would do this:
[ 1, 1, 1, 2, 1, 1 ]
.tally # { 1=>5, 2=>1 }
.find { |_, v| v == 1 } # [2, 1]
.first # 2
Or as 3limin4t0r suggested:
[ 1, 1, 1, 2, 1, 1 ]
.tally # { 1=>5, 2=>1 }
.invert[1] # { 5=>1, 1=>2 } => 2
The following doesn't use tallies and will short circuit the search when a unique item is found. First, it returns nil if the array has fewer than 3 elements, since there's no way to answer the question in that case. If that check is passed, it works by comparing adjacent values. It performs an up-front check that the first two elements are equal—if not, it checks against the third element to see which one is different. Otherwise, it iterates through the array and returns the first value it finds which is unequal. It returns nil if there is not a distinguished element in the array.
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] == arr[1]
arr.each.with_index do |x, i|
i += 1
return arr[i] if arr[i] != x
end
elsif arr[1] == arr[2]
arr[0]
else
arr[1]
end
end
This also works with non-numeric arrays such as find_uniq(%w(c c c d c c c c)).
Thanks to Cary Swoveland for reminding me about each_cons. That can tighten up the solution considerably:
def find_uniq(arr)
return nil if arr.size < 3
if arr[0] != arr[1]
return arr[1] == arr[2] ? arr[0] : arr[1]
end
arr.each_cons(2) { |x, y| return y if y != x }
end
For all but tiny arrays this method effectively has the speed of Enumerable#find.
def find_uniq(arr)
multi = arr[0,3].partition { |e| e == arr.first }
.sort_by { |e| -e.size }.first.first
arr.find { |e| e != multi }
end
find_uniq [1, 1, 1, 2, 1, 1] #=> 2
find_uniq [0, 0, 0.55, 0, 0] #=> 0.55
find_uniq [:pig, :pig, :cow, :pig] #=> :cow
The wording of the question implies the array contains at least three elements. It certainly cannot be empty or have two elements. (If it could contain one element add the guard clause return arr.first if arr.size == 1.)
I examine the first three elements to determine the object that has duplicates, which I assign to the variable multi. I then am able to use find. find is quite fast, in part because it short-circuits (stops enumerating the array when it achieves a match).
If
arr = [1, 1, 1, 2, 1, 1]
then
a = arr[0,3].partition { |e| e == arr.first }.sort_by { |e| -e.size }
#=> [[1, 1, 1], []]
multi = a.first.first
#=> 1
If any of these:
arr = [2, 1, 1, 1, 1, 1]
arr = [1, 2, 1, 1, 1, 1]
arr = [1, 1, 2, 1, 1, 1]
apply then
a = arr[0,3].partition { |e| e == arr.first }.sort_by { |e| -e.size }
#=> [[1, 1], [2]]
multi = a.first.first
#=> 1
Let's compare the computational performace of the solutions that have been offered.
def spickermann1(arr)
arr.tally.find { |_, v| v == 1 }.first
end
def spickermann2(arr)
arr.tally.invert[1]
end
def spickermann3(arr)
arr.tally.min_by(&:last).first
end
def pjs(arr)
if arr[0] == arr[1]
arr.each.with_index do |x, i|
i += 1
return arr[i] if arr[i] != x
end
elsif arr[1] == arr[2]
arr[0]
else
arr[1]
end
end
I did not include #3limin4t0r's solution because of the author's admission that it is relatively inefficient. I did include, however, include two variants of #spikermann's answer, one ("spickermann2") having been proposed by #3limin4t0r in a comment.
require 'benchmark'
def test(n)
puts "\nArray size = #{n}"
arr = Array.new(n-1,0) << 1
Benchmark.bm do |x|
x.report("Cary") { find_uniq(arr) }
x.report("spickermann1") { spickermann1(arr) }
x.report("spickermann2") { spickermann2(arr) }
x.report("spickermann3") { spickermann3(arr) }
x.report("PJS") { pjs(arr) }
end
end
test 100
Array size = 100
user system total real
Cary 0.000032 0.000009 0.000041 ( 0.000029)
spickermann1 0.000022 0.000015 0.000037 ( 0.000019)
spickermann2 0.000017 0.000002 0.000019 ( 0.000016)
spickermann3 0.000019 0.000002 0.000021 ( 0.000018)
PJS 0.000042 0.000025 0.000067 ( 0.000034)
test 10_000
Array size = 10_000
user system total real
Cary 0.001101 0.000091 0.001192 ( 0.001119)
spickermann1 0.000699 0.000096 0.000795 ( 0.000716)
spickermann2 0.000794 0.000071 0.000865 ( 0.000896)
spickermann3 0.000776 0.000081 0.000857 ( 0.000781)
PJS 0.001140 0.000113 0.001253 ( 0.001300)
test 1_000_000
Array size = 1_000_000
user system total real
Cary 0.061148 0.000787 0.061935 ( 0.063022)
spickermann1 0.043598 0.000474 0.044072 ( 0.044590)
spickermann2 0.044909 0.000663 0.045572 ( 0.046371)
spickermann3 0.042907 0.000210 0.043117 ( 0.043162)
PJS 0.072766 0.000226 0.072992 ( 0.073168)
I attribute the apparent superiority of #spickermann's answer to the fact that Enumerable#tally has no block to evaluate (unlike, for example, Enumerable#find in my answer).
Your code can be fixed by using find instead of each:
def find_uniq(arr)
arr.uniq.find { |e| arr.count(e) == 1 }
end
However this is quite inefficient since uniq needs to iterate the full collection. After finding the unique values the arr collection is iterated 1 or 2 more times by count (assuming there are only two unique values), depending on the position of the values in the uniq result.
For simple solution I suggest looking at the answer of spickermann which only iterates the full collection once.
For your specific scenario you could technically increase performance by short-circuiting the tally. This is done by manually tallying and breaking the loop if the tally contains 2 distinct values and at least 3 items are tallied.
def find_uniq(arr)
tally = Hash.new(0)
arr.each_with_index do |item, index|
break if tally.size == 2 && index >= 3
tally[item] += 1
end
tally.invert[1]
end
In this code if user type 2, two times and 1, two times. Then there's two maximum elements and both Kinder and Twix should be printed. But how ? I probably can do this with if method but this will make my code even longer. Any cool version? Can I do this with just one if?
a = [0, 0, 0,]
b = ["Kinder", "Twix", "Mars"]
while true
input = gets.chomp.to_i
if input == 1
a[0] += 1
elsif input == 2
a[1] += 1
elsif input == 3
a[2] += 1
elsif input == 0
break
end
end
index = a.index(a.max)
chocolate = b[index] if index
print a.max,chocolate
The question really has nothing to do with how the array a is constructed.
def select_all_max(a, b)
mx = a.max
b.values_at(*a.each_index.select { |i| a[i] == mx })
end
b = ["Kinder", "Twix", "Mars"]
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
See Array#values_at.
This could alternatively be done in a single pass.
def select_all_max(a, b)
b.values_at(
*(1..a.size-1).each_with_object([0]) do |i,arr|
case a[i] <=> arr.last
when 0
arr << i
when 1
arr = [i]
end
end
)
end
p select_all_max [0, 2, 1], b
["Twix"]
p select_all_max [2, 2, 1], b
["Kinder", "Twix"]
p select_all_max [1, 1, 1], b
["Kinder", "Twix", "Mars"]
One way would be as follows:
First, just separate the input-gathering from the counting, so we'll just gather input in this step:
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
Now we can tally up the inputs. If you have Ruby 2.7 you can simply do counts_by_input = inputs.tally to get { "Twix" => 2, "Kinder" => 2 }. Otherwise, my preferred approach is to use group_by with transform_values:
counts_by_input = inputs.group_by(&:itself).transform_values(&:count)
# => { "Twix" => 2, "Kinder" => 2 }
Now, since we're going to be extracting values based on their count, we want to have the counts as keys. Normally we might invert the hash, but that won't work in this case because it will only give us one value per key, and we need multiple:
inputs_by_count = counts_by_input.invert
# => { 2 => "Kinder" }
# This doesn't work, it removed one of the values
Instead, we can use another group_by and transform_values (the reason I like these methods is because they're very versatile ...):
inputs_by_count = counts_by_input.
group_by { |input, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
# => { 2 => ["Twix", "Kinder"] }
The transform_values code here is probably a bit confusing, but one important thing to understand is that often times, calling Enumerable methods on hashes converts them to [[key1, val1], [key2, val2]] arrays:
counts_by_input.group_by { |input, count| count }
# => { 2 => [["Twix", 2], ["Kinder", 2]] }
Which is why we call transform_values { |keyvals| keyvals.map(&:first) } afterwards to get our desired format { 2 => ["Twix", "Kinder"] }
Anyway, at this point getting our result is very easy:
inputs_by_count[inputs_by_count.keys.max]
# => ["Twix", "Kinder"]
I know this probably all seems a little insane, but when you get familiar with Enumerable methods you will be able to do this kind of data transformation pretty fluently.
Tl;dr, give me the codez
inputs = []
loop do
input = gets.chomp.to_i
break if input.zero?
inputs << input
end
inputs_by_count = inputs.
group_by(&:itself).
transform_values(&:count).
group_by { |keyvals, count| count }.
transform_values { |keyvals| keyvals.map(&:first) }
top_count = inputs_by_count.keys.max
inputs_by_count[top_count]
# => ["Twix", "Kinder"]
How about something like this:
maximum = a.max # => 2
top_selling_bars = a.map.with_index { |e, i| b[i] if e == maximum }.compact # => ['Kinder', 'Twix']
p top_selling_bars # => ['Kinder', 'Twix']
If you have
a = [2, 2, 0,]
b = ['Kinder', 'Twix', 'Mars']
You can calculate the maximum value in a via:
max = a.max #=> 2
and find all elements corresponding to that value via:
b.select.with_index { |_, i| a[i] == max }
#=> ["Kinder", "Twix"]
I'm looking for algorithm which will create all subsets of array like if array is
A = [1, 4, 6]
algorithm will generate:
[1,4,6,[1,4], [1,6], [4, 6], [1, 4,6]]
Note : I'm looking for help with algorithm not some function.
You can build a backtracking based solution to formulate all the possible sub-sequence for the given array. Sharing an ideone link for the same: https://ideone.com/V0gCDX
all = []
def gen(A, idx = 0, cur = []):
if idx >= len(A):
if len(cur): all.append(cur)
return
gen(A, idx + 1, list(cur))
incl = list(cur)
incl.append(A[idx])
gen(A, idx + 1, incl)
def solve(A):
global all
all = []
gen(A)
return all
print(solve([1, 4, 6]))
Is there a sensible way to do the following:
I want to take an array and select specific items from the array according to conditions, removing them from the array as they go.
(I basically want to split the contents of an array into categories).
array = [1,2,3,4,5,6,7,8]
less_than_three = array.reject_destructively{|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = array.reject_destructively{|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
I've tried delete_if, select!, reject! and none of them seem to be able to give you the affected items whilst leaving the array with the rest.
Unless I'm going mad, which is entirely possible.
As I understood the question, you do not want to produce two new objects. Here you go:
class Array
def carve!
dup.tap { delete_if &Proc.new } - self
end
end
array = [1,2,3,4,5,6,7,8]
p array.carve! { |v| v < 3 }
#⇒ [1, 2] # returned by Array#carve method
p array
#⇒ [3, 4, 5, 6, 7, 8] # remained in original array
Using this solution, array.__id__ remains the same. And this is the golfiest answer all around :)
You can build your own method for this...
class Array
def extract(&block)
temp = self.select(&block)
self.reject!(&block)
temp
end
end
then...
a = [1, 2, 3, 4, 5]
a.extract{|x| x < 3}
=> [1,2]
p a
=> [3, 4, 5]
EDIT: If you don't want to monkey patch (but monkey patching isn't evil in itself) you can do it with a vanilla method...
def select_from_array(array, &block)
temp = array.select(&block)
array.reject!(&block)
temp
end
array = [1,2,3,4,5,6,7,8]
less_than_three = select_from_array(array){|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = select_from_array(array){|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
In rails 6 there is a method extract!:
a = [1, 2, 3] #=> [1, 2, 3]
a.extract! { |num| num.odd? } #=> [1, 3]
a #=> [2]
irb(main):001:0> array = [1,2,3,4,5,6,7,8]
=> [1, 2, 3, 4, 5, 6, 7, 8]
irb(main):002:0> array.partition{|v| v < 3}
=> [[1, 2], [3, 4, 5, 6, 7, 8]]
is there a specific reason, why this has to be destructive ?
Will this help
class Array
def reject_destructively(&block)
arr = self.select(&block)
arr.each{ |i| self.delete(i) }
arr
end
end
array = [1,2,3,4,5,6,7,8]
p less_than_three = array.reject_destructively{|v| v<3}
#=> [1,2]
p array
#=> [3,4,5,6,7,8]
p more_than_five = array.reject_destructively{|v| v>5}
#=> [6,7,8]
p array
#=> [3,4,5]
The above code can be simplified further to look like:
class Array
def reject_destructively(&block)
self.select(&block).each{ |i| self.delete(i) }
end
end
Ok. This works, avoids monkey patching, keeps it to one line...etc, but it's damn ugly....
less_than_three = array.dup - array.reject!{|v| v<3}
=> [1,2]
array
=> [3,4,5,6,7,8]
more_than_five = array.dup - array.reject!{|v| v>5}
=> [6,7,8]
array
=> [3,4,5]
module Enumerable
def reject_destructively
array=[]
self.each do |y|
if yield(y)
array<<y
end
end
array.each do |x|
self.delete(x)
end
return array
end
end
array=[10,9,2,1,3,45,52]
print less_than_three = array.reject_destructively{|v| v < 3}
print array
You can use group_by to get all of the elements that satisfy the condition in one group, and all of the rest in the other.
For example
[1,2,3,4,5].group_by{|i| i > 3}
gives
{false=>[1, 2, 3], true=>[4, 5]}
More information is available at http://ruby-doc.org/core-2.1.1/Enumerable.html#method-i-group_by
I have:
arr = [1, 2, 3, 4, 5]
The code below replaces the odd elements of arr into symbols corresponding to them:
0 = '+'
1 = '-'
2 = '*'
3 = '/'
4 = '^'
so 12345 would turn into 1*3^5.
Code:
sym = ['+', '-', '*', '/', '^']
elements = 1
while elements < 5
arr[elements] = sym[arr[elements]]
elements += 2
end
puts "#{arr.join}"
I am looking for a more efficient replacement to this code.
You could spiff-up your method as follows:
(1...arr.size).step(2).each { |i| arr[i] = sym[arr[i]] }
arr.join
#=> "1*3^5"
Here's another way:
arr.each_slice(2).flat_map { |x,y| y ? [x, sym[y]] : [x] }.join
#=> "1*3^5"
And another:
enum = [true, false].cycle
arr.map { |e| enum.next ? e : sym[e] }.join
#=> "1*3^5"
Your method mutates (modifies) arr. Do you want to do that? The last two methods do not (but could be easily modified to do so).
Which of these three methods is best? You tell me!
I don't think you'll find a faster solution, but you may find a more elegant one. This is how I'd approach this:
arr = [1, 2, 3, 4, 5]
sym = ['+', '-', '*', '/', '^']
arr.map!.with_index { |value, idx| idx.odd? ? sym[idx] : value }
This has the same effect as your code, but it should work on arrays of any size (provided they are the same length).