Select numbers by their first n digits - ruby

I have an array of numbers:
array = [234, 928234, 234932]
I need to count how many of the elements in array have the first three digits as 234; i.e., the result should count in array[0] and array[2].
Is there a way to use .count? If I use array.count("234"), it will count in all elements. Is there something I can add to array.count{ } to make it work?

You'd have to stringify each element before you can make that kind of comparison:
[234, 928234, 234932].count { |num| num.to_s.start_with? '234' }
See also: String#start_with?

Integer#digits returns an Array of Integers representing the digits of an Integer. The digits are returned least-significant place first, so what you'll want to do is look at the last three elements of the Array and compare them to [4, 3, 2]:
array.count {|n| n.digits[-3..-1] == [4, 3, 2] }
#=> 2

Like Jorg is doing you can pass a block to #count and in that block convert the number to a string then use simple string match.
array = [234, 928234, 234932, 234444234, 234, 2345]
array.count{|x| x.to_s.match(/^234/)}
5

Related

Is there a function to find the max values index in an array containing ints and strings?

In the example here, is there a way to print out the max int (7654) index (5) properly?
I havent been able to figure out a way to do this with an array containing ints and strings, only ones with strictly ints.
array = ["me", 2345, "you", 345, "him", 7654, "her", 25]
arraybutonlynumbers = [2345, 345, 7654, 25]
puts array.each_with_index.max[1] #comparison of Array with Array failed (ArgumentError)
puts arraybutonlynumbers.each_with_index.max[1] #no error
Using Select, Max, and Index
You can find the intermediate results you need in three conceptual steps using built-in Array methods:
Use Array#select to consider only Integer values.
Capture the largest Integer value with Array#max.
Search the original Array for the captured Integer, and return the index of that element with Array#index.
You would then use some or all of those return values to craft your expected output. To illustrate the general approach from the irb console:
# find largest integer in a mixed array
array.select { |e| e.is_a? Integer }.max
#=> 7654
# find index of last return value
array.index _
#=> 5
However, to get the output you want, you'll need to refactor this into something that keeps the intermediate results so you can return them in the format you expect. For example:
def max_integer_with_index array
max_int = array.select { |e| e.is_a? Integer }.max
max_int_idx = array.index max_int
[max_int, max_int_idx]
end
max_integer_with_index [
"me", 2345, "you", 345, "him", 7654, "her", 25
]
#=> [7654, 5]
You can also reduce finding the index to a single line of code if you don't need the intermediate values. For example:
array.index array.select { |e| e.is_a? Integer }.max
#=> 5
Caveat
Please note that if you want to do something else besides ignore the String objects in your array, you will probably need to implement Array#sort_by (inherited from Enumerable; see also Comparable) to draw your own custom comparisons between Integers and Strings.
array = ["me", 2345, "you", 345, "him", 7654, "her", 25]
element, index = array.each_with_index.max_by{| el, idx| el.to_i}
p element, index
# => 7654
# => 5
Note however that to_i converts strings which do not start with a digit to 0, which may produce unwanted results if there are no positive integers in the array.

Returning a non negative integer in descending order

I'm working on a Ruby challenge to take any non-negative integer as an argument and return it with its digits in descending order. Essentially, rearrange the digits to create the highest possible number.
for example:
Input: 145263 Output: 654321
Input: 123456789 Output: 987654321
Currently this is what my solution looks like:
def descending_order(n)
# take any non-negative integer as an argument
# return it with digits in descending order
n.sqrt(1){ |digits| digits.sort_by.reverse }
end
However, it keeps throwing an error message saying:
`descending_order': undefined method `sqrt' for 0:Integer (NoMethodError)
def descending_order(n)
n.to_s.split(//).sort.reverse.join.to_i
end
This creates an array of strings, each string being a single digit as text. We can use the normal sort here, because we can reasonably assume that the collating sequence of the digits in an encoding obeys the same order than their numeric counterpart. In particular, we know that i.e. '4' < '8'.
I think the easiest to understand answer is:
n = 145263
n.digits.sort.reverse.join.to_i
n.digits => [3, 6, 2, 5, 4, 1]
n.digits.sort => [1, 2, 3, 4, 5, 6]
n.digits.sort.reverse => [6, 5, 4, 3, 2, 1]
n.digits.sort.reverse.join => "654321"
n.digits.sort.reverse.join.to_i => 654321
Summary of methods
digits => Returns the digits of ints place-value representation with radix base (default: 10). The digits are returned as an array with the least significant digit as the first array element.
sort => Returns a new array created by sorting self.
reverse => Returns a new array containing selfs elements in reverse order.
join => Returns a string created by converting each element of the array to a string, separated by the given separator. If the separator is nil, it uses current $,. If both the separator and $, are nil, it uses an empty string.
to_i => Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36).
I like Tom Lord's answer in his comment. It's a bit cryptic at first, but Stefan's explanation is excellent.

Ruby's Array Combination Method

I am going through the problems on Ruby Monk's Ruby Primer.
Problem Statement
Given a 3 or 4 digit number with distinct digits, return a sorted array of all the unique numbers that can be formed with those digits.
Example:
Given: 123
Return: [123, 132, 213, 231, 312, 321]
I thought that the Array#combination method would do the trick. My code looks like this:
def number_shuffle(number)
# take integer and turn it into an array of digits
digits = Array.new
number.to_s.split('').each do |element|
digits << element.to_i
end
# shuffle the elements
return digits.combination(digits.length).to_a
end
puts number_shuffle(123)
But the code above returns:
1
2
3
Not sure what I'm doing wrong here. I thought the documentation made it clear:
http://www.ruby-doc.org/core-2.2.0/Array.html#method-i-combination
Any help is appreciated.
Instead of Array#combination, you want Array#permutation:
number = 123
number.to_s.split('').permutation.map(&:join).uniq.sort
# => ["123", "132", "213", "231", "312", "321"]
number = 122
number.to_s.split('').permutation.map(&:join).uniq.sort
# => ["122", "212", "221"]
You can get the permutations of the character array using Array#permutation:
def number_shuffle(number)
number.to_s.chars.permutation.map { |x| x.join.to_i }.sort
end
For the ruby monk question what u need is the Array.permutations. Array.permutation(n) is the number of possible arrangements of an array taking n at a time.
[1,2,3] with n = 1 will be 1, 2, 3
[1,2,3] with n = 2 will be [1,2] [2,1] [1,3] [3,1] [2,3] [3,2]
What you need is
Array.permutations(Array.length)
Array.combination(n) returns the number of unique selections that can be made from the array when taking n objects out of the array.
for an Array [1,2,3] if n = 1. You can only take out one element at a time
the possible selections are 1 ,2 and 3.
for an Array [1,2,3] if n= 2. You can take out two elements at a time.
the possible selections are [1,2] , [1,3] and [2,3]
You have given the length of the Array as N (N = Array.Length)
So in the case of [1,2,3] if n = 3, There is only one way to make a
selection using all the elements.
That is [1,2,3]. This is why your code only returns one combination.

Code to write a random array of x numbers with no duplicates [duplicate]

This is what I have so far:
myArray.map!{ rand(max) }
Obviously, however, sometimes the numbers in the list are not unique. How can I make sure my list only contains unique numbers without having to create a bigger list from which I then just pick the n unique numbers?
Edit:
I'd really like to see this done w/o loop - if at all possible.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a can be replaced with any array.
0 is "minvalue", 50 is "max value"
x is "how many values i want out"
of course, its impossible for x to be permitted to be greater than max-min :)
In expansion of how this works
(0..5).to_a ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 } ==> [0, 1, 2, 4, 3, 5] # constant
[0,1,2,3,4,5].sort{ 1 } ==> [5, 3, 0, 4, 2, 1] # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 } ==> [1, 5, 0, 3, 4, 2 ] # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ] ==> [1, 5, 0 ]
Footnotes:
It is worth mentioning that at the time this question was originally answered, September 2008, that Array#shuffle was either not available or not already known to me, hence the approximation in Array#sort
And there's a barrage of suggested edits to this as a result.
So:
.sort{ rand() - 0.5 }
Can be better, and shorter expressed on modern ruby implementations using
.shuffle
Additionally,
[0..x]
Can be more obviously written with Array#take as:
.take(x)
Thus, the easiest way to produce a sequence of random numbers on a modern ruby is:
(0..50).to_a.shuffle.take(x)
This uses Set:
require 'set'
def rand_n(n, max)
randoms = Set.new
loop do
randoms << rand(max)
return randoms.to_a if randoms.size >= n
end
end
Ruby 1.9 offers the Array#sample method which returns an element, or elements randomly selected from an Array. The results of #sample won't include the same Array element twice.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
When compared to the to_a.sort_by approach, the sample method appears to be significantly faster. In a simple scenario I compared sort_by to sample, and got the following results.
require 'benchmark'
range = 0...1000000
how_many = 5
Benchmark.realtime do
range.to_a.sample(how_many)
end
=> 0.081083
Benchmark.realtime do
(range).sort_by{rand}[0...how_many]
end
=> 2.907445
Just to give you an idea about speed, I ran four versions of this:
Using Sets, like Ryan's suggestion.
Using an Array slightly larger than necessary, then doing uniq! at the end.
Using a Hash, like Kyle suggested.
Creating an Array of the required size, then sorting it randomly, like Kent's suggestion (but without the extraneous "- 0.5", which does nothing).
They're all fast at small scales, so I had them each create a list of 1,000,000 numbers. Here are the times, in seconds:
Sets: 628
Array + uniq: 629
Hash: 645
fixed Array + sort: 8
And no, that last one is not a typo. So if you care about speed, and it's OK for the numbers to be integers from 0 to whatever, then my exact code was:
a = (0...1000000).sort_by{rand}
Yes, it's possible to do this without a loop and without keeping track of which numbers have been chosen. It's called a Linear Feedback Shift Register: Create Random Number Sequence with No Repeats
[*1..99].sample(4) #=> [64, 99, 29, 49]
According to Array#sample docs,
The elements are chosen by using random and unique indices
If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
How about a play on this? Unique random numbers without needing to use Set or Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
You could use a hash to track the random numbers you've used so far:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Rather than add the items to a list/array, add them to a Set.
If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.
Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:
require 'set'
def rand_n(n, max)
randoms = Set.new
i = 0
loop do
randoms << rand(max)
break if randoms.size > n
i += 1
end
puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
return randoms.to_a
end
I could write this code to LOOK more like Array.map if you want :)
Based on Kent Fredric's solution above, this is what I ended up using:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
Thanks Kent.
No loops with this method
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Here is one solution:
Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that
r+list[i-1] does not over flow
r > 0
For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.
The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.
As far as it is nice to know in advance the maxium value, you can do this way:
class NoLoopRand
def initialize(max)
#deck = (0..max).to_a
end
def getrnd
return #deck.delete_at(rand(#deck.length - 1))
end
end
and you can obtain random data in this way:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
you'll obtain nil when all the values will be exausted from the deck.
Method 1
Using Kent's approach, it is possible to generate an array of arbitrary length keeping all values in a limited range:
# Generates a random array of length n.
#
# #param n length of the desired array
# #param lower minimum number in the array
# #param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
Method 2
Another, possibly more efficient, method modified from same Kent's another answer:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
Output
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)

How do I generate a list of n unique random numbers in Ruby?

This is what I have so far:
myArray.map!{ rand(max) }
Obviously, however, sometimes the numbers in the list are not unique. How can I make sure my list only contains unique numbers without having to create a bigger list from which I then just pick the n unique numbers?
Edit:
I'd really like to see this done w/o loop - if at all possible.
(0..50).to_a.sort{ rand() - 0.5 }[0..x]
(0..50).to_a can be replaced with any array.
0 is "minvalue", 50 is "max value"
x is "how many values i want out"
of course, its impossible for x to be permitted to be greater than max-min :)
In expansion of how this works
(0..5).to_a ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 } ==> [0, 1, 2, 4, 3, 5] # constant
[0,1,2,3,4,5].sort{ 1 } ==> [5, 3, 0, 4, 2, 1] # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 } ==> [1, 5, 0, 3, 4, 2 ] # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ] ==> [1, 5, 0 ]
Footnotes:
It is worth mentioning that at the time this question was originally answered, September 2008, that Array#shuffle was either not available or not already known to me, hence the approximation in Array#sort
And there's a barrage of suggested edits to this as a result.
So:
.sort{ rand() - 0.5 }
Can be better, and shorter expressed on modern ruby implementations using
.shuffle
Additionally,
[0..x]
Can be more obviously written with Array#take as:
.take(x)
Thus, the easiest way to produce a sequence of random numbers on a modern ruby is:
(0..50).to_a.shuffle.take(x)
This uses Set:
require 'set'
def rand_n(n, max)
randoms = Set.new
loop do
randoms << rand(max)
return randoms.to_a if randoms.size >= n
end
end
Ruby 1.9 offers the Array#sample method which returns an element, or elements randomly selected from an Array. The results of #sample won't include the same Array element twice.
(1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
When compared to the to_a.sort_by approach, the sample method appears to be significantly faster. In a simple scenario I compared sort_by to sample, and got the following results.
require 'benchmark'
range = 0...1000000
how_many = 5
Benchmark.realtime do
range.to_a.sample(how_many)
end
=> 0.081083
Benchmark.realtime do
(range).sort_by{rand}[0...how_many]
end
=> 2.907445
Just to give you an idea about speed, I ran four versions of this:
Using Sets, like Ryan's suggestion.
Using an Array slightly larger than necessary, then doing uniq! at the end.
Using a Hash, like Kyle suggested.
Creating an Array of the required size, then sorting it randomly, like Kent's suggestion (but without the extraneous "- 0.5", which does nothing).
They're all fast at small scales, so I had them each create a list of 1,000,000 numbers. Here are the times, in seconds:
Sets: 628
Array + uniq: 629
Hash: 645
fixed Array + sort: 8
And no, that last one is not a typo. So if you care about speed, and it's OK for the numbers to be integers from 0 to whatever, then my exact code was:
a = (0...1000000).sort_by{rand}
Yes, it's possible to do this without a loop and without keeping track of which numbers have been chosen. It's called a Linear Feedback Shift Register: Create Random Number Sequence with No Repeats
[*1..99].sample(4) #=> [64, 99, 29, 49]
According to Array#sample docs,
The elements are chosen by using random and unique indices
If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):
require 'securerandom'
[*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
How about a play on this? Unique random numbers without needing to use Set or Hash.
x = 0
(1..100).map{|iter| x += rand(100)}.shuffle
You could use a hash to track the random numbers you've used so far:
seen = {}
max = 100
(1..10).map { |n|
x = rand(max)
while (seen[x])
x = rand(max)
end
x
}
Rather than add the items to a list/array, add them to a Set.
If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.
Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:
require 'set'
def rand_n(n, max)
randoms = Set.new
i = 0
loop do
randoms << rand(max)
break if randoms.size > n
i += 1
end
puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
return randoms.to_a
end
I could write this code to LOOK more like Array.map if you want :)
Based on Kent Fredric's solution above, this is what I ended up using:
def n_unique_rand(number_to_generate, rand_upper_limit)
return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1]
end
Thanks Kent.
No loops with this method
Array.new(size) { rand(max) }
require 'benchmark'
max = 1000000
size = 5
Benchmark.realtime do
Array.new(size) { rand(max) }
end
=> 1.9114e-05
Here is one solution:
Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that
r+list[i-1] does not over flow
r > 0
For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.
The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.
As far as it is nice to know in advance the maxium value, you can do this way:
class NoLoopRand
def initialize(max)
#deck = (0..max).to_a
end
def getrnd
return #deck.delete_at(rand(#deck.length - 1))
end
end
and you can obtain random data in this way:
aRndNum = NoLoopRand.new(10)
puts aRndNum.getrnd
you'll obtain nil when all the values will be exausted from the deck.
Method 1
Using Kent's approach, it is possible to generate an array of arbitrary length keeping all values in a limited range:
# Generates a random array of length n.
#
# #param n length of the desired array
# #param lower minimum number in the array
# #param upper maximum number in the array
def ary_rand(n, lower, upper)
values_set = (lower..upper).to_a
repetition = n/(upper-lower+1) + 1
(values_set*repetition).sample n
end
Method 2
Another, possibly more efficient, method modified from same Kent's another answer:
def ary_rand2(n, lower, upper)
v = (lower..upper).to_a
(0...n).map{ v[rand(v.length)] }
end
Output
puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected
puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params
puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range
puts (ary_rand 5, 9, 0).to_s # [] no such range :)

Resources