Ruby array subtraction? - ruby

Either I don't understand what happens when you subtract an array from an array, or something is wrong here.
What I have is a list of usernames (strings) in an array:
users.count - users.uniq.count # => 9
users - users.uniq # => []
I'm not sure how this is possible.
I'm essentially trying to find a list of the duplicates. I realize there are other ways to go about this, just trying to understand Array operations better.
Here is the workaround code I used to get the same:
users.inject(Hash.new(0)) {|h,i| h[i] += 1; h}.select{|k,v| v > 1}

You could use
dups = users.select{|e| users.count(e) > 1 }.uniq
Or, to find only a single duplicate element:
firstDup = users.detect {|e| users.count(e) > 1 }
About the array subtraction, this may clarify:
a = [1, 1, 1, 1, 1]
a - [1] # => []
Array subraction removes all occurences, not just one.

The behavior of Array#- is quite correct. It
Returns a new array that is a copy of the original array, removing any
items that also appear in other_ary.
The user list (with duplicates) without all the users is empty.
Coming from "Ruby: How to find and return a duplicate value in array?", the following seems to be good at finding duplicates in an Array:
users.detect {|e| users.rindex(e) != users.index(e) }

Related

Ruby randomly shuffle an array so that no element keeps its original place

I have an array that stores some names. Each person solves a task and then I want to assign each solution to a different person for verification. In short this means that I need to perform a shuffle in an array in a way that no element keeps its original place. The solution I thought of is to perform a shuffle in the array until the second condition is met and here is the code for it:
copied = names.dup
loop do
copied.shuffle!
valid = true
(0...copied.size).each do |i|
if copied[i] == names[i]
valid = false
break
end
end
break if valid
end
puts copied
Still I feel there may be a more optimal solution to this problem. Anyone has a better idea?
Looks like what you're trying to do is to create a map of {verifier => task_solver} where verifier != task_solver. A simple way to achieve this is simply have each person's verify the next person's task:
verifiers = {}
task_solvers.each_with_index do |task_solver, index|
verifiers[task_solver] = task_solvers[(index + 1) % task_solvers.size]
end
If you want a little bit more randomisation, you could use this, which uses the same algorithm but just shuffles your list before anything happens:
verifiers = {}
shuffled_task_solvers = task_solvers.shuffle
shuffled_task_solvers.each_with_index do |task_solver, index|
verifiers[task_solver] = shuffled_task_solvers[(index + 1) % shuffled_task_solvers.size]
end
What you are after might be called a derangement. Take a look here: http://rosettacode.org/wiki/Permutations/Derangements (There is an example in Ruby).
I think you need just one shuffle. After that if some element in the copied array is in its original place you can swap it with one of its neighbours (it is clear that new places of the two elements cannot be the same as original places).
Here is what I came up with. I don't know if it's any better though :P
list = [1,2,3,4,5,6,7,8]
def jumble (array)
new = array.shuffle
array.each_with_index do |item, index|
if new[index] == item
new[index], new[index - 1] = new[index - 1], new[index]
end
end
new
end
new = jumble (list)
puts new.inspect
Output:
[4, 1, 8, 3, 7, 2, 6, 5]

Extracting multiple entries of a Ruby array

Let's say I have an array
arry = ["a","b","c","d","e","f","g"]
and I want to get the fourth, second, and seventh element of that array.
Let's say some other function determined which are the needed values and passed me backan array of indices like [3, 1, 6]. In most languages I could write this
array[3,1,6] #=> ["d","b","g"] (or "dbg"?)
or
array[3 1 6] #=> ["d","b","g"]
But that won't work in Ruby, of course. Is there a simple way to do this in Ruby? The cleanest I can find is something like:
[3,1,6].map { |i| array[i] }
I really tried to find a duplicate to this question since it seems so basic, but I just couldn't.
This is remarkably easy to do in most languages, so I'm almost assuming I'm just overlooking the remarkably obvious.
array.values_at(3,1,6) #=> ["d","b","g"]
If your other function returns an array then you must flatten it before you can pass it as parameters to values_at. Like this
arry = ["a","b","c","d","e","f","g"]
def indices
[1,3,6]
end
puts arry.values_at(*indices)
output
b
d
g

Index a ruby array to omit an element or range of elements?

I have a ruby array of, say, 10 elements, and I'd like to return all but the 5th element.
a = *(1..10)
I'd like to be able to do something like
a[0..3 + 5..9]
Or even better something like (borrowing from the R syntax),
a[-4]
to do this, but that doesn't work. (Nor does anything more clever I've tried like getting an array of the element indices). What's the best way to do this in ruby?
You can use values_at: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-values_at
so you may use it as
a.values_at(0..3, 5..9)
No black magic required:
a[0..3] + a[5..9]
Look in the documentation under Array#delete_at
For example, myarray.delete_at(5) results in the array lacking what was element 5 (counting from 0, of course).
Example:
class Array
def without(n)
self.delete_at(n)
return self
end
end
arr = [1, 2, 3, 4]
p arr.without(1) #=> [1, 3, 4]
However, as a commenter points out, this alters the original array, which may not be what the O.P. wants! If that's an issue, write it like this:
class Array
def without(n)
arr2 = Array.new(self)
arr2.delete_at(n)
return arr2
end
end
That returns the desired array (an array lacking the nth element of the original) while leaving the original array untouched.

Delete one of many from Array

I have an Array that contains some elements multiple times. Now I want to create a new Array with one of those elements deleted.
Example:
a = [1,1,1,2]
delete_index = a.find_index(1)
result = a.clone
result.delete_at(delete_index)
# result is now [1,1,2]
This code looks really ugly for such an easy task. I had a look at the methods that Array provides, but couldn't find a better way of doing this.
a.delete_at(a.index(1) || a.length)
a.length handles the case where your element isn't found; because it's out of range, nothing will be deleted and your return value wil be nil.
If part of your question was to do this to a copy of the array, just call it on a clone:
a2 = a.clone ; a2.delete_at(...)
If you want to do this for each duplicated element, you can chain it to a block that selects the duplicated elements:
a.select { |e| array.count(e) > 1 }.each { |dup| a.delete_at a.index(dup) }
You could monkey patch Array:
class Array
def delete_first_occurrence(o)
delete_at(find_index(o) || length)
self
end
end
a = [1,1,1,2]
result = a.clone.delete_first_occurrence(1)
=> [1, 1, 2]
I can't quite tell, but it sounds like you're just trying to remove duplicates from the array. If that's the case, it's as easy as array.uniq, which will return a new array with all duplicates removed. If you'd like to modify the original array in place, you can use array.uniq! instead.
If that's not what you're trying to accomplish, please update your question with some example input and output of what you're trying to accomplish.

Ruby: Selecting a set of array indices whose elements pass a test

Ruby has a select method that takes an array and returns a subarray consisting of all the elements that pass the test given in a block:
myarray.select{|e| mytest(e)} #=> subarray of elements passing mytest
I am wondering whether there is a simple method to get not these elements, but their indices. I understand you could do this:
indices = []
myarray.each_with_index{|e,i| indices << i if mytest(e)}
But I'm looking for a one-liner. Does one exist? Please don't write an extension to the Array class, I know you can get a one-liner that way.
Another one-liner:
(0...myarray.length).select {|i| mytest(myarray[i])}
Cheers!
Here's a one-liner for you. It selects indexes of elements whose length is 3.
a = ['foo', 'bar', 't']
a.map.with_index{|el, i| i if el.length == 3}.compact # => [0, 1]
Or another one (suggested by #fl00r):
a.reduce([]){|ar,el| ar << a.index(el) if el.size == 3; ar}
Also,
myarray.select{|e| mytest(e)}.map!{|e| myarray.index(e)}
However, this won't work properly if you have any repeated elements.

Resources