Related
Currently, I'm having print like this
print ((stamp_amount[0], 'first mark') unless stamp_amount[0].zero?), (', ' if !stamp_amount[0].zero? && !stamp_amount[1].zero?),
((stamp_amount[1], 'second mark') unless stamp_amount[1].zero?)
stamp_amount is an array with 2 integer values
Let's say in the current situation stamp_amount[0] = 10 and stamp_amount[1] = 3
Output preview:
10 first mark, 3 second mark
So if stamp_amount[0] = 0 the 10 first mark, part won't be show. Same if stamp_amount[1] = 0 the , 3 second mark part won't be shown
For me, it seems a little bit incorrect in terms of theory. Could you please suggest me the more correct or less painful print of this? :)
Cheers!
Your code is trying to join a sequence of up to two elements with a separator. The joining is a solved problem, see Array#join.
The problem can be then reduced to "how can I produce the correct sequence, given my stamp_amount input". Now this can be done in a thousand ways. Here's one:
def my_print(stamp_amount)
ary = [
!stamp_amount[0].zero? && stamp_amount[0],
!stamp_amount[1].zero? && stamp_amount[1],
].select{|elem| elem }
ary.join(', ')
end
my_print([10, 3]) # => "10, 3"
my_print([0, 3]) # => "3"
my_print([10, 0]) # => "10"
my_print([0, 0]) # => ""
Here's another
ary = []
ary << stamp_amount[0] unless stamp_amount[0].zero?
ary << stamp_amount[1] unless stamp_amount[1].zero?
ary.join(', ')
Here's yet another. This version can handle stamp_amount of any length.
ary = stamp_amount.reject(&:zero?)
ary.join(', ')
I'd go with the third, but the second one may be the easiest to understand for a beginner.
Use the select, as an alternative to reject (shown in part 3 of the answer by Sergio Tulentsev). It is just asa readable, and depending on the context and on the future changes to the code, you may prefer one versus the other.
puts stamp_amount.select{ |a| !a.zero? }.join(", ")
A few examples of inputs and outputs are:
stamp_amount output
--------------------------------------------------------------------------
10, 3 10, 3
10, 0 10
0, 3 3
0, 0 (prints an empty line, because the selected array is empty)
You're calculating zero? on index points more often than is needed, but the first thing I would look at refactoring here is the readability of the code. It might be nicer to calculate the message to print outside of the print method and explain what is happening with variable names.
# rubocop is going to complain about variable assignment like this
first_amount, second_amount = *stamp_amount
We can actually use the reason rubocop prefers the .zero? over == 0 or .empty? method to guide our development. zero? is in essence just empty? but it communicates the meaning of what you are attempting to do in a better manner. I would use this reasoning when assigning strings to variables that explain what they are doing.
some_name_that_explains_what_this_is_0 = "#{first_amount} piecu centu marka"
some_name_that_explains_what_this_is_1 = "#{second_amount} tris centu marka"
Your current code is confusing as you have the possibility of printing a string like "10 tris centu marka" which does not make lexical sense and probably not what you are after considering tis evaluates to 'second mark', which would pose an issue if the first value is zero. We also could reject zero integers before we start converting them to strings.
array = [1, 0].reject(&:zero?)
Now we can take the array and do something like:
string = []
array.each_with_index { |e, i| string << "#{e} #{Ordinalize.new(i).ordinalize} mark" }
message = string.join(', ')
print(message)
# ord class
class Ordinalize
def initialize(value)
#value = value
end
def ordinalize
mapping[#value]
end
def mapping
# acounting for zero index
['first', 'second']
end
end
where we are calculating the ordinalization and letting our new class handle the sentence structure for us.
Outputs:
[1, 0] => "1 first mark"
[0, 1] => "1 first mark"
[1, 2] => "1 first mark, 2 second mark"
I am trying to return the index's to all occurrences of a specific character in a string using Ruby. A example string is "a#asg#sdfg#d##" and the expected return is [1,5,10,12,13] when searching for # characters. The following code does the job but there must be a simpler way of doing this?
def occurances (line)
index = 0
all_index = []
line.each_byte do |x|
if x == '#'[0] then
all_index << index
end
index += 1
end
all_index
end
s = "a#asg#sdfg#d##"
a = (0 ... s.length).find_all { |i| s[i,1] == '#' }
require 'enumerator' # Needed in 1.8.6 only
"1#3#a#".enum_for(:scan,/#/).map { Regexp.last_match.begin(0) }
#=> [1, 3, 5]
ETA: This works by creating an Enumerator that uses scan(/#/) as its each method.
scan yields each occurence of the specified pattern (in this case /#/) and inside the block you can call Regexp.last_match to access the MatchData object for the match.
MatchData#begin(0) returns the index where the match begins and since we used map on the enumerator, we get an array of those indices back.
Here's a less-fancy way:
i = -1
all = []
while i = x.index('#',i+1)
all << i
end
all
In a quick speed test this was about 3.3x faster than FM's find_all method, and about 2.5x faster than sepp2k's enum_for method.
Here's a long method chain:
"a#asg#sdfg#d##".
each_char.
each_with_index.
inject([]) do |indices, (char, idx)|
indices << idx if char == "#"
indices
end
# => [1, 5, 10, 12, 13]
requires 1.8.7+
Another solution derived from FMc's answer:
s = "a#asg#sdfg#d##"
q = []
s.length.times {|i| q << i if s[i,1] == '#'}
I love that Ruby never has only one way of doing something!
Here's a solution for massive strings. I'm doing text finds on 4.5MB text strings and the other solutions grind to a halt. This takes advantage of the fact that ruby .split is very efficient compared to string comparisions.
def indices_of_matches(str, target)
cuts = (str + (target.hash.to_s.gsub(target,''))).split(target)[0..-2]
indicies = []
loc = 0
cuts.each do |cut|
loc = loc + cut.size
indicies << loc
loc = loc + target.size
end
return indicies
end
It's basically using the horsepower behind the .split method, then using the separate parts and the length of the searched string to work out locations. I've gone from 30 seconds using various methods to instantaneous on extremely large strings.
I'm sure there's a better way to do it, but:
(str + (target.hash.to_s.gsub(target,'')))
adds something to the end of the string in case the target is at the end (and the way split works), but have to also make sure that the "random" addition doesn't contain the target itself.
indices_of_matches("a#asg#sdfg#d##","#")
=> [1, 5, 10, 12, 13]
During my lessons of Ruby I came across of this exercise. I'm trying to remove 3 or more the same charactes in a row. Test Cases Input: abbbaaccada Output: ccada Input: bbccdddcb Output: (Empty string)
So far I have solution which doesn't return expected results:
def playground("abbbaaccada")
count = string.length
string.chars.each_with_index.map { |v, i| (v * (count - i)).capitalize }.join('')
end
output gives me
==> AaaaaaaaaaaBbbbbbbbbbBbbbbbbbbBbbbbbbbAaaaaaaAaaaaaCccccCcccAaaDdA
instead of
==> ccada
Could you please advise?
Edit:
Forgot to add that regexp isn't allowed
There are two challenges here:
Match and remove any run of thee or more characters in a row
Recurse to test again in case the previous step created a new run of three
Here's one way to do it:
THREE_OR_MORE = /(.)\1{2,}/
def three_is_too_many(str)
if str.match? THREE_OR_MORE
str = three_is_too_many(str.gsub(THREE_OR_MORE, ''))
end
str
end
The regexp finds any character ('.'), followed by itself ('\1'), two or more times ('{2,}').
Then the routine either a) removes three or more and tests again or b) returns the string.
Here's a potential solution. The following method searches for any subsequence of an array with repeats, and returns the range of the repeated values if there are three or more of them.
def find_3_or_more(ary)
ary.each_index do |i|
j = i + 1
while j < ary.length && ary[i] == ary[j]
j += 1
end
return (i...j) if j - i > 2
end
nil
end
This portion breaks the target string into an array of chars, and repeatedly slices out the characters in ranges identified as repeats until there are none, as indicated by a nil range.
def delete_3_or_more(str)
ary = str.chars
while r = find_3_or_more(ary)
ary.slice!(r)
end
return ary.join
end
It seems to do the job for your test cases.
def recursively_remove_runs_of_3_or_more(str)
arr = str.chars
loop do
a = arr.slice_when { |a,b| a.downcase != b.downcase }.to_a
b = a.reject! { |e| e.size > 2 }
arr = a.flatten
break arr.join if b.nil?
end
end
recursively_remove_runs_of_3_or_more "abbbaaccada"
#=> "ccada"
This uses Enumerable#slice_when (new in MRI v2.2). Note that Array#reject! returns nil when no changes are made.
You could alternatively use Enumerable#chunk_while (new in MRI v2.3). Simply replace:
a = arr.slice_when { |a,b| a.downcase != b.downcase }.to_a
with:
a = arr.chunk_while { |a,b| a.downcase == b.downcase }.to_a
chunk_while and slice_when are yin and yang.
If a regular expression could be used and case where not an issue, you could write:
str = "abbbaaccada"
s = str.dup
loop { break(s) if s.gsub!(/(.)\1{2,}/, '').nil? }
#=> "ccada"
(I wanted to comment, but it doesn't allow me to do that yet.)
Assuming that you are trying to learn, I chose to only give you some tips while avoiding a solution.
There might be shorter ways of doing this using regex or/and some String methods. However, you said you can not use regex.
My tip is, try to solve it only using the sections you have covered so far. It may not necessarily be the most elegant solution, but you can revise it as you progress. As others suggested, recursion might be a good option. But, if you are not familiar with that yet, you can try slicing the string and merging the parts you need. This can be combined with an endless loop to check the new string satisfies your condition: but think about when you need to break out of the loop.
Also, in your code:
v * (count - i)
String#* actually gives you count - i copies of v, concatenated together.
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]
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]