Is there a way to iterate over a range, excluding a value - ruby

I need to iterate over an array, excluding a value in the middle. I know I can do
(0..10).each do |i|
unless i==6
...
end
end
But I'm wondering if there's a cleaner way. The closest solution I found was this:
Is there an elegant way to exclude the first value of a range?
I need to do this to iterate through poorly organized data I was given.

((0..10).to_a - [6]).each do |i|
...
end

You can make usage of reject method to accomplish it.
I don't know if its a cleaner way but it is just another way.
(0..10).reject { |v| v == 6 }.each do |i|
Give a shot and tell me what do you think about it. :)

Not sure is it cleaner or preferred way for your simple case, but you can do something like
(0..10).select{|i| i!=6}.each do
# YOUR CODE
end
in more complex cases this is definitely a way
P.S. while writing the answer, the answer with reject one arrived :) this is almost the same one, i just forgot of reject, remembered only select :)

You can make use of next if you just want to get around wrapping your code in an unless block. For example, if you want to skip a set of numbers, you can do something like:
skip_values = [3, 6, 7]
(0..10).each do |i|
# When i is 3, 6, or 7, continue to the next iteration
next if skip_values.include?(i)
...
end

I suppose you could define a subclass or patch Range itself with something like...
def reach not_this_one
each do |x|
yield x unless x == not_this_one
end
end
...or perhaps...
def reach not_this_one, &block
self.select { |x| x != not_this_one }.each &block
end

You can temporarily exclude a value by using Enumerable#grep_v:
(1..5).grep_v(2).each { |n| puts n }
#1
#3
#4
#5
Unfortunately grep_v like some of the other posted answers creates an intermediate array. To be honest I prefer your initial approach, which can be written more neatly (without monkey-patching) as:
(1..5).each { |n| puts n unless n == 2 }
#1
#3
#4
#5

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]

Ruby Do Multiple Things With Each Element of an Array

I know that I can use
my_array = [1,2,3,4,5]
my_array.each {|element| puts element}
to do something with each element of an array but what if I need to do several things with each element? It starts complaining when I try to put multiple statements in the block. What I am really looking for is something more like this:
my_array = [1,2,3,4,5]
my_array.each |element| do
#operation one involving the element
#operation two involving the element
...
end
Is there any good way to achieve this effect?
You can put as many statements as you like inside a block, but you need to get the do/end syntax right.
The order is do |elemenet|, not |element| do. The do/end keywords replace the {}.
my_array.each do |element|
puts "element is #{element}"
element += 1
puts "Now element is #{element}"
# etc...
end
If you really want to cram it into a one liner you can use semicolons.
x = [1,2,3,4,5]
x.map{|y| y*=2; y-=5; y}
This gives you:
=> [-3, -1, 1, 3, 5]
It gets pretty ugly pretty fast though, so use multiliners unless there's a really good reason you want it on one line.

Find value in an array of hashes

taglist = [{:name=>"Daniel_Xu_Forever", :tag=>["helo", "world"]},
{:name=>"kcuf", :tag=>["hhe"]},
{:name=>"fine", :tag=>[]},
{:name=>"how hare you", :tag=>[]},
{:name=>"heki", :tag=>["1", "2", "3"]},
{:name=>"railsgirls", :tag=>[]},
{:name=>"_byoy", :tag=>[]},
{:name=>"ajha", :tag=>[]},
{:name=>"nimei", :tag=>[]}]
How to get specified name's tag from taglist
For example , I want to extract user "fine"'s tag?
Could this be achieved without do iterator?
This will return the contents of the :tag key for any users name which == 'fine'
taglist.select { |x| x[:name] == 'fine' }.map { |u| u[:tag] }
First you select out only the users you are interested with .select.
And then use .map to collect an array of only what you want.
In this case the end result will be: []
Is do really an iterator?
taglist.find{|tl| tl[:name] == 'fine'}[:tag]
Just to be silly how about:
eval taglist.to_s[/:name=>"fine", :tag=>(.*?)}/, 1]
#=> []
No, it cannot be done without a loop.
And even if you find a solution where your code avoids a loop, for sure the library function that you're calling will include a loop. Finding an element in an array requires a loop. Period.
For example, take this (contrived) example
pattern = "fine"
def pattern.===(h); self == h[:name]; end
taglist.grep(pattern)
which does not seem to use a loop, but calls grep which is implemented using a loop.
Or another, equally contrived, example
class Hash; def method_missing(sym); self[sym]; end; end
taglist.group_by(&:name)["fine"]
which again does seem to call group_by without a loop, but actually it does.
So the answer is, no.
So my first answer missed the no do rule.
Here is an answer that doesn't use a do block.
i=0
begin
if taglist[i][:name] == 'fine'
tag = taglist[i][:tag]
break
end
i+=1
end while i < taglist.length - 0
Technically I think this is still using a block. But probably satisfies the restriction.

Ruby find in array with offset

I'm looking for a way to do the following in Ruby in a cleaner way:
class Array
def find_index_with_offset(offset, &block)
[offset..-1].find &block
end
end
offset = array.find_index {|element| element.meets_some_criterion?}
the_object_I_want =
array.find_index_with_offset(offset+1) {|element| element.meets_another_criterion?}
So I'm searching a Ruby array for the index of some object and then I do a follow-up search to find the first object that matches some other criterion and has a higher index in the array. Is there a better way to do this?
What do I mean by cleaner: something that doesn't involve explicitly slicing the array. When you do this a couple of times, calculating the slicing indices gets messy fast. I'd like to keep operating on the original array. It's easier to understand and less error-prone.
NB. In my actual code I haven't monkey-patched Array, but I want to draw attention to the fact that I expect I'm duplicating existing functionality of Array/Enumerable
Edits
Fixed location of offset + 1 as per Mladen Jablanović's comment; rewrite error
Added explanation of 'cleaner' as per Mladen Jablanović's comment
Cleaner is here obviously subjective matter. If you aim for short, I don't think you could do better than that. If you want to be able to chain multiple such finds, or you are bothered by slicing, you can do something like this:
module Enumerable
def find_multi *procs
return nil if procs.empty?
find do |e|
if procs.first.call(e)
procs.shift
next true if procs.empty?
end
false
end
end
end
a = (1..10).to_a
p a.find_multi(lambda{|e| e % 5 == 0}, lambda{|e| e % 3 == 0}, lambda{|e| e % 4 == 0})
#=> 8
Edit: And if you're not concerned with the performance you could do something like:
array.drop_while{|element|
!element.meets_some_criterion?
}.drop(1).find{|element|
element.meets_another_criterion?
}

First order array difference in Ruby

What's the slickest, most Ruby-like way to do this?
[1, 3, 10, 5].diff
should produce
[2, 7, -5]
that is, an array of first order differences. I've come up with a solution which I'll add below, but it requires ruby 1.9 and isn't all that slick. what else is possible?
I like this functional style:
module Enumerable
def diff
each_cons(2).map {|pair| pair.reverse.reduce :-}
end
end
EDIT: I just realized that the reverse is totally unnecessary. If this were a functional language, I would have used pattern matching, but Ruby doesn't support pattern matching. It does, however, support destructuring bind, which is a good enough approximation for pattern matching in this case.
each_cons(2).map {|first, second| second - first}
No smiley, though.
I like how this sounds if you just read it out loud from left to right: "For each pair, apply the difference between the first and second elements of the pair." In fact, I normally don't like the name collect and prefer map instead, but in this case that reads even better:
each_cons(2).collect {|first, second| second - first}
"For each pair, collect the difference between its elements." Sounds almost like a definition of first order difference.
Yet another way..Seems the shortest so far:)
module Enumerable
def diff
self[1..-1].zip(self).map {|x| x[0]-x[1]}
end
end
The concept comes from functional programming, of course:
module Enumerable
def diff
self.inject([0]) { |r,x| r[-1] += x; r << -x } [1..-2]
end
end
[1,3,10,5].diff
Note that you don't need any separate intermediate variables here
Here's the fastest way I could find (faster than all the others suggested here as of this moment, in both 1.8 and 1.9):
module Enumerable
def diff
last=nil
map do |x|
r = last ? x - last : nil
last = x
r
end.compact
end
end
With this close runner-up:
module Enumerable
def diff
r = []
1.upto(size-1) {|i| r << self[i]-self[i-1]}
r
end
end
Of the others here, testr's self-described "feeble" attempt is the next fastest, but it's still slower than either of these.
And if speed is no object, here's my aesthetic favorite:
module Enumerable
def diff!
[-shift+first] + diff! rescue []
end
def diff
dup.diff!
end
end
But this is (for reasons I don't entirely understand) an order of magnitude slower than any other suggestion here!
Minor variation on Jörg W Mittag's:
module Enumerable
def diff
each_cons(2).map{|a,b| b-a}
end
end
# Attempt, requires ruby 1.9.
module Enumerable
def diff
each_cons(2).with_object([]){|x,array| array << x[1] - x[0]}
end
end
Example:
[1,3,10,5].diff
=> [2, 7, -5]
Another way to do it.
module Enumerable
def diff
result = []
each_with_index{ |x, i|
return result if (i == (self.length-1))
result << self[i+1] - x
}
end
end
My feeble attempt...
module Enumerable
def diff
na = []
self.each_index { |x| r << self[x]-self[x-1] if x > 0 }
na
end
end
p [1,3,10,5].diff #returned [2, 7, -5]

Resources