Ruby - list minus - ruby

I have two list
a = [1, 2, 3, 4, 1, 2]
b = a.uniq # b = [1, 2, 3, 4]
c = a - b
the result gets c = []
I expects the result c = [1, 2]
How can I get this?

This is the expected result according to the Array documentation.
If you want to get the values which have multiple occurrences within the array you could something like:
a.uniq.keep_if {|v| a.count(v) > 1}
If Ruby 1.8 is used, keep_if is not available, so you need to use something like:
a.uniq.delete_if {|v| a.count(v) == 1}
(Both methods keep a intact)

If you want to get elements from array which appears more than once you can use following code:
a.delete_if { |x| a.count(x) <= 1 }.uniq
If you want to perform sub operation on two arrays based on elements position you can use following code:
i = 0
a.drop_while { |x| b[i+=1] == x }.uniq

From the documentation
Array Difference—Returns a new array that is a copy of the original array, removing any items that also appear in other_ary. (If you need set-like behavior, see the library class Set.)

You are looking for all duplicates or the opposite of uniq? Maybe this link will help: Identify duplicates in an array

Looks like you are trying to find elements that have duplicates. Here are several ways, pick the one you like:
a.select { |x| a.count(x) > 1 }.uniq
a.sort.each_cons(2).map { |x,y| x if x == y }.compact
a.group_by{|x|x}.values.select{|k|k.size>1}.map(&:first)

Related

How do I filter elements in an array?

Sample Array:
x = [1,2,3,4,2,2,2]
Filter:
y = [2,4,7,9]
Desired output:
result = [2,4,2,2,2]
I tried:
result = (x & y)
but this gives me [4,2].
How do I get: result = [2,4,2,2,2]?
How about:
x - (x - y)
#=> [2, 4, 2, 2, 2]
1-2 lines longer than #Mark's answer, but more efficient (if both arrays are large):
require 'set'
keep = Set[2,4,7,9] # or Set.new(some_large_array)
result = x.select{ |n| keep.include?(n) } #=> [2, 4, 2, 2, 2]
The problem with writing...
x.select{ |i| y.include?(i) }
...is that this is O(x*y) the the number of elements in each array. With 100 elements in each you are doing 10,000 operations in the worst case; my answer does only 100 operations.
First, don't capitalize variables in Ruby. Capitalization is for constants, like class names.
result = x.select {|i| y.include? i}
Note that select is also called find_all, and is the positive filter in ruby; the negative filter is reject. Between the braces you can put any code you want; it will be run once for each item of x (the item is passed in as an argument and becomes i), and the result of the whole call will include all the elements for which the block returns a true value.

Ruby inject with index and brackets

I try to clean my Code. The first Version uses each_with_index. In the second version I tried to compact the code with the Enumerable.inject_with_index-construct, that I found here.
It works now, but seems to me as obscure as the first code.
Add even worse I don't understand the brackets around element,index in
.. .inject(groups) do |group_container, (element,index)|
but they are necessary
What is the use of these brackets?
How can I make the code clear and readable?
FIRST VERSION -- WITH "each_with_index"
class Array
# splits as good as possible to groups of same size
# elements are sorted. I.e. low elements go to the first group,
# and high elements to the last group
#
# the default for number_of_groups is 4
# because the intended use case is
# splitting statistic data in 4 quartiles
#
# a = [1, 8, 7, 5, 4, 2, 3, 8]
# a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]
#
# b = [[7, 8, 9], [4, 5, 7], [2, 8]]
# b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index do |element, index|
group_number = (index.to_f / average_group_size).floor
groups[group_number] << element
end
groups
end
end
SECOND VERSION -- WITH "inject" AND index
class Array
def sorted_in_groups(number_of_groups = 4)
groups = Array.new(number_of_groups) { Array.new }
return groups if size == 0
average_group_size = size.to_f / number_of_groups.to_f
sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort
sorted.each_with_index.inject(groups) do |group_container, (element,index)|
group_number = (index.to_f / average_group_size).floor
group_container[group_number] << element
group_container
end
end
end
What is the use of these brackets?
It's a very nice feature of ruby. I call it "destructuring array assignment", but it probably has an official name too.
Here's how it works. Let's say you have an array
arr = [1, 2, 3]
Then you assign this array to a list of names, like this:
a, b, c = arr
a # => 1
b # => 2
c # => 3
You see, the array was "destructured" into its individual elements. Now, to the each_with_index. As you know, it's like a regular each, but also returns an index. inject doesn't care about all this, it takes input elements and passes them to its block as is. If input element is an array (elem/index pair from each_with_index), then we can either take it apart in the block body
sorted.each_with_index.inject(groups) do |group_container, pair|
element, index = pair
# or
# element = pair[0]
# index = pair[1]
# rest of your code
end
Or destructure that array right in the block signature. Parentheses there are necessary to give ruby a hint that this is a single parameter that needs to be split in several.
Hope this helps.
lines = %w(a b c)
indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
acc << ind - 1 if el == "b"
acc
end
indexes # => [0]
What is the use of these brackets?
To understand the brackets, first you need to understand how destruction works in ruby. The simplest example I can think of this this:
1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
1.8.7 :002 > puts a, b
1.8.7 :003?> end
1
3
2
4
You should know how each function works, and that the block receives one parameter. So what happens when you pass two parameters? It takes the first element [1,3] and try to split (destruct) it in two, and the result is a=1 and b=3.
Now, inject takes two arguments in the block parameter, so it is usually looks like |a,b|. So passing a parameter like |group_container, (element,index)| we are in fact taking the first one as any other, and destructing the second in two others (so, if the second parameter is [1,3], element=1 and index=3). The parenthesis are needed because if we used |group_container, element, index| we would never know if we are destructing the first or the second parameter, so the parenthesis there works as disambiguation.
9In fact, things works a bit different in the bottom end, but lets hide this for this given question.)
Seems like there already some answers given with good explanation. I want to add some information regards the clear and readable.
Instead of the solution you chose, it is also a possibility to extend Enumerable and add this functionality.
module Enumerable
# The block parameter is not needed but creates more readable code.
def inject_with_index(memo = self.first, &block)
skip = memo.equal?(self.first)
index = 0
self.each_entry do |entry|
if skip
skip = false
else
memo = yield(memo, index, entry)
end
index += 1
end
memo
end
end
This way you can call inject_with_index like so:
# m = memo, i = index, e = entry
(1..3).inject_with_index(0) do |m, i, e|
puts "m: #{m}, i: #{i}, e: #{e}"
m + i + e
end
#=> 9
If you not pass an initial value the first element will be used, thus not executing the block for the first element.
In case, someone is here from 2013+ year, you have each_with_object and with_index for your needs:
records.each_with_object({}).with_index do |(record, memo), index|
memo[record.uid] = "#{index} in collection}"
end

Modify particular items in array while traversing in reverse order in Ruby

Is there a way to modify particular array elements (based on some condition) while traversing it in reverse order in Ruby?
To be more clear lets say,
problem is replace even numbers in [1,2,3,4,5] with x
output should be [1,x,3,x,5] (same array) but replace should happen from right to left..traversing from 5 to 1.
Thanks in Advance!
This works: (arr.length -1).downto(0) { |x| do something with arr[x] }
p [1,2,3,4,5].reverse_each.map{|e| e.odd? ? e : e/2} #[5, 2, 3, 1, 1]
I understand you want to traverse in reverse order, not get the output also reversed. Maybe this:
xs = [1, 2, 3]
xs.reverse_each.with_index { |x, idx| xs[xs.size-1-idx] = x.to_s if x == 2 }
xs #=> [1, "2", 3]
I appreciate and love Ruby's humane syntax, but you may want to consider more verbose options such as:
ary = [1,2,3,4,5]
i = ary.count - 1
while i >= 0 do
ary[i] = "x" if ary[i] % 2 == 0
i -= 1
end
puts ary.join(",")

Rails 3 - Remove items belonging to array B from array A

I'm starting to play with arrays, but i'm stuck with something that seems yet very simple...
I'm trying to remove x elements belonging to one array from another array.
I've looked at this but .... blocked :
Deleting items from an array requires multiple passes to remove them all
a = ["1","2","3","4","5","6"]
b = ["1","3"]
c = a.reject { |i| i =~ /b/ }
Well, i'm a bit lot here, thanks!
a = ["1","2","3","4","5","6"]
b = ["1","3"]
c = a - b
same as
c = a.reject{ |e| b.include? e }
If you want to modify an existing array by removing elements in another array you can use minus equals.
a = [1, 2, 3, 1, 4]
b = [1, 4]
a -= b
a
=> [2, 3]
Also keep in mind that subtracting an array of elements from another array will remove all occurrences of those elements not just the first occurrence.

Skip over iteration in Enumerable#collect

(1..4).collect do |x|
next if x == 3
x + 1
end # => [2, 3, nil, 5]
# desired => [2, 3, 5]
If the condition for next is met, collect puts nil in the array, whereas what I'm trying to do is put no element in the returned array if the condition is met. Is this possible without calling delete_if { |x| x == nil } on the returned array?
My code excerpt is heavily abstracted, so looking for a general solution to the problem.
There is method Enumerable#reject which serves just the purpose:
(1..4).reject{|x| x == 3}.collect{|x| x + 1}
The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.
BTW, map (or collect) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable.
Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject (or its similar method named each_with_object):
(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
I would simply call .compact on the resultant array, which removes any instances of nil in an array. If you'd like it to modify the existing array (no reason not to), use .compact!:
(1..4).collect do |x|
next if x == 3
x
end.compact!
In Ruby 2.7+, it’s possible to use filter_map for this exact purpose. From the docs:
Returns an array containing truthy elements returned by the block.
(0..9).filter_map {|i| i * 2 if i.even? } #=> [0, 4, 8, 12, 16]
{foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } #=> [:foo, :baz]
For the example in the question: (1..4).filter_map { |x| x + 1 unless x == 3 }.
See this post for comparison with alternative methods, including benchmarks.
just a suggestion, why don't you do it this way:
result = []
(1..4).each do |x|
next if x == 3
result << x
end
result # => [1, 2, 4]
in that way you saved another iteration to remove nil elements from the array. hope it helps =)
i would suggest to use:
(1..4).to_a.delete_if {|x| x == 3}
instead of the collect + next statement.
You could pull the decision-making into a helper method, and use it via Enumerable#reduce:
def potentially_keep(list, i)
if i === 3
list
else
list.push i
end
end
# => :potentially_keep
(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]

Resources