How to assert list of values in ascending order? - ruby

Here is a list of values in an array:
[463, 246, 216, 194, 154, 152, 147, 140, 129, 128, 123, 118, 118, 102, 102, 101, 97, 96, 93, 85]
How can I ensure/assert through RSpec that the array list is in ascending order?

The simplest way is probably:
expect(array.sort).to eq(array)

"Ascending" means "the next element is not smaller than the current". You can encode that into a predicate easily:
expect(array.each_cons(2).all? {|a, b| a <= b }).to be_truthy
Note that Array#sort is not stable, so something like
expect(array.sort).to eq(array)
does not work!

Related

Store numbers in a list that has a range

Write a program that will store the numbers from a list called random_number which are less than or equal to 300 into a new list. Print the results.
random_numbers = [100, 34, 10, 17, 111, 304, 99, 87, 55, 0, 5, 303, 399, 354, 121, 208, 267, 406, 13]
My attempt
rand_count=0
for rand_value in random_numbers:
if rand_value <=300:
print (rand_value) rand_count+=1
else:
random_numbers.append(rand_value)

Efficient calculations of paths with shared subpaths in directed graph

I need a recommendation for an efficient datastructure that suits my problem of calculating the path costs inside a directed graph under the contraint, that many paths sometimes share a same subpath for which I do not want to make the calculations twice.
Each number in these lists are refering to a node inside a directed graph. Each line describes one path:
[121, 85, 135, 99, 141, 134, 4, 33, 65, 131, 18, 127],
[121, 85, 135, 99, 141, 134, 65, 33, 4, 127],
[121, 85, 135, 99, 141, 134, 65, 33, 4, 131, 18, 127],
[121, 85, 135, 99, 141, 134, 65, 33, 4, 107, 127],
[121, 85, 135, 99, 141, 134, 65, 23, 18, 127],
[121, 85, 135, 99, 141, 134, 65, 23, 18, 131, 4, 127],
[121, 85, 135, 99, 141, 134, 65, 23, 18, 131, 4, 107, 127],
[121, 85, 135, 99, 141, 134, 65, 107, 4, 127],
[121, 85, 135, 99, 141, 134, 65, 107, 4, 131, 18, 127],
[121, 85, 135, 99, 141, 134, 65, 107, 127],
[121, 85, 135, 99, 141, 134, 65, 131, 18, 127],
[121, 85, 135, 99, 141, 134, 65, 131, 4, 127],
[121, 85, 135, 99, 141, 134, 65, 131, 4, 107, 127],
[121, 85, 135, 99, 141, 4, 127],
...
As one can see in this example many paths share the same subset of nodes along the way. (And many other paths [not shown here] do not share a subpath.)
I want to compute the 'optimality' of each path, i.e. the sum OR products of the weights along the way. These weights can be seen as constant for my graph while traversing each path.
To be clear: I want to avoid calculating the weight of the path from node 121 to node 141 for all shown paths in this example, as these multiplications / additions of the weights between two nodes is the same (up to node 141) in each of the shown paths...
I am satisfied with a datastructure recommendation and explanation why the mentioned datastructure is best suited for my needs.
If you also have a library recommendation I would prefer one in C/C++ or Python.

Ruby: using `.each` or `.step`, step forward a random amount for each iteration

(Also open to other similar non-Rails methods)
Given (0..99), return entries that are randomly picked in-order.
Example results:
0, 5, 11, 13, 34..
3, 12, 45, 67, 87
0, 1, 2, 3, 4, 5.. (very unlikely, of course)
Current thought:
(0..99).step(rand(0..99)).each do |subindex|
array.push(subindex)
end
However, this sets a single random value for all the steps whereas I'm looking for each step to be random.
Get a random value for the number of elements to pick, randomly get this number of elements, sort.
(0..99).to_a.sample((0..99).to_a.sample).sort
#⇒ [7, 20, 22, 29, 45, 48, 57, 61, 62, 76, 80, 82]
Or, shorter (credits to #Stefan):
(0..99).to_a.sample(rand(0..99)).sort
#⇒ [7, 20, 22, 29, 45, 48, 57, 61, 62, 76, 80, 82]
Or, in more functional manner:
λ = (0..99).to_a.method(:sample)
λ.(λ.()).sort
To feed exactly N numbers:
N = 10
(0..99).to_a.sample(N).sort
#⇒ [1, 5, 8, 12, 45, 54, 60, 65, 71, 91]
There're many ways to achieve it.
For example here's slow yet simple one:
# given `array`
random_indexes = (0...array.size).to_a.sample(rand(array.size))
random_indexes.sort.each { |i| puts array[i] }
Or why don't you just:
array.each do |value|
next if rand(2).zero?
puts value
end
Or you could use Enumerator#next random number of times.
Below example returns a sorted array with random entries from given range based on randomly picked true or false from array [true, false]:
(0..99).select { [true, false].sample }
=> [0, 3, 12, 13, 14, 17, 20, 24, 26, 28, 30, 32, 34, 35, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 56, 58, 59, 60, 61, 62, 65, 67, 69, 70, 71, 79, 81, 84, 86, 91, 93, 94, 95, 98, 99]
To reduce the chances of a bigger array being returned, you can modify your true/false array to include more falsey values:
(0..99).select { ([true] + [false] * 9).sample }
=> [21, 22, 28, 33, 37, 58, 59, 63, 77, 85, 86]

Ruby Newbie Needs to Convert a String to Byte Array with Some Padding

in Ruby I have a string like this:
myString = "mystring"
I want to convert the string to a byte array taking only the first 16 bytes and pad with 0's if shorter.
I can do this the brute force way. But...
Care to share a 'cool' way?
Something like this? You should probably check for edge cases like multibyte chars.
"my string"[0..15].ljust(16,'0')
You can get the string as a byte array by calling bytes on it, then once you have it as a byte array, you can take the first 16 elements. Finally, you pad the array by filling it with a range as the second argument:
def padded_byte_array(string, length = 16)
bytes = string.bytes.take(length)
bytes.fill(0, bytes.length...length)
end
and then you can call it:
padded_byte_array('my string')
# => [109, 121, 32, 115, 116, 114, 105, 110, 103, 0, 0, 0, 0, 0, 0, 0]
padded_byte_array('some super long string longer than 16 bytes')
# => [115, 111, 109, 101, 32, 115, 117, 112, 101, 114, 32, 108, 111, 110, 103, 32]
padded_byte_array('本当に長いマルチバイト文字列')
# => [230, 156, 172, 229, 189, 147, 227, 129, 171, 233, 149, 183, 227, 129, 132, 227]
I assume that if arr.size < str.size, where str is the string and arr is the array to be returned, str.bytes is returned. If, in that case, str.bytes[0, [str.size, arr.size].min] is to be returned, that requires an obvious adjustment.
def padded_bytes(str, arr_size)
str_bytes = str.bytes
Array.new([arr_size, str.size].max) { |i| str_bytes.fetch(i, 0) }
end
padded_bytes("tiger", 8)
#=> [116, 105, 103, 101, 114, 0, 0, 0]
padded_bytes("tiger", 3)
#=> [116, 105, 103, 101, 114]
Thanks folks for your answers.
In the end, I implemented
ba = name[0..15].ljust(16, "\0").bytes.to_a
Aza gave the closest to what I asked.
My original looked like this:
ba = name[0..15].bytes.to_a
while ba.length < 16 do ba.push(0) end
Until I got your answers. Thanks again!

Sort ActiveRecord query response by Globalize3 translation on an association using locale

I have two models and three tables total which are involved. Unfortunately, some renaming of the models from the original table names has occurred, so pardon the confusion.:
Symptom (chief_complaints)
SymptomName (chief_complaint_names)
(chief_complaint_name_translations)
The latter, of course, is used by Globalize3 to translate the :name attribute of the SymptomName model. Symptom has_many :symptom_names.
Consider the following:
Symptom.includes(names: :translations).order("chief_complaint_name_translations.name ASC")
This returns a mostly correct list, but it's sorting the list of Symptoms by the first translation name it encounters (despite my locale), and listing them by the correct locale.
# .to_sql output
"SELECT `chief_complaints`.* FROM `chief_complaints` INNER JOIN `chief_complaint_names` ON `chief_complaint_names`.`chief_complaint_id` = `chief_complaints`.`id` INNER JOIN `chief_complaint_name_translations` ON `chief_complaint_names`.`id` = `chief_complaint_name_translations`.`chief_complaint_name_id` ORDER BY chief_complaint_name_translations.name, chief_complaint_name_translations.name ASC"
# Actual SQL generated in the console
SELECT `chief_complaints`.* FROM `chief_complaints` INNER JOIN `chief_complaint_names` ON `chief_complaint_names`.`chief_complaint_id` = `chief_complaints`.`id` INNER JOIN `chief_complaint_name_translations` ON `chief_complaint_names`.`id` = `chief_complaint_name_translations`.`chief_complaint_name_id` ORDER BY chief_complaint_name_translations.name, chief_complaint_name_translations.name ASC
SELECT `chief_complaint_names`.`id` AS t0_r0, `chief_complaint_names`.`chief_complaint_id` AS t0_r1, `chief_complaint_names`.`created_at` AS t0_r2, `chief_complaint_names`.`updated_at` AS t0_r3, `chief_complaint_name_translations`.`id` AS t1_r0, `chief_complaint_name_translations`.`chief_complaint_name_id` AS t1_r1, `chief_complaint_name_translations`.`locale` AS t1_r2, `chief_complaint_name_translations`.`name` AS t1_r3, `chief_complaint_name_translations`.`created_at` AS t1_r4, `chief_complaint_name_translations`.`updated_at` AS t1_r5, `chief_complaint_name_translations`.`url` AS t1_r6 FROM `chief_complaint_names` LEFT OUTER JOIN `chief_complaint_name_translations` ON `chief_complaint_name_translations`.`chief_complaint_name_id` = `chief_complaint_names`.`id` WHERE `chief_complaint_name_translations`.`locale` = 'en' AND `chief_complaint_names`.`chief_complaint_id` IN (173, 2, 1, 224, 223, 3, 75, 4, 186, 15, 199, 201, 5, 177, 245, 94, 219, 225, 241, 6, 228, 213, 234, 164, 88, 26, 81, 7, 74, 136, 57, 21, 28, 18, 163, 165, 8, 112, 183, 147, 9, 160, 10, 64, 218, 170, 200, 207, 11, 175, 13, 138, 72, 12, 214, 239, 248, 14, 150, 190, 137, 16, 17, 154, 178, 127, 56, 206, 246, 101, 19, 20, 22, 96, 172, 255, 23, 24, 216, 25, 215, 29, 125, 113, 198, 195, 244, 27, 247, 132, 232, 70, 135, 133, 30, 31, 34, 32, 197, 181, 222, 208, 243, 35, 227, 196, 33, 36, 179, 53, 131, 126, 159, 58, 37, 202, 203, 38, 120, 68, 220, 230, 176, 39, 226, 148, 174, 91, 40, 41, 145, 151, 134, 189, 73, 43, 42, 47, 93, 44, 45, 46, 209, 192, 204, 205, 48, 188, 128, 49, 212, 249, 250, 211, 153, 50, 51, 52, 139, 187, 237, 109, 156, 129, 54, 157, 55, 87, 69, 84, 146, 60, 149, 221, 231, 242, 229, 59, 194, 240, 155, 61, 158, 62, 171, 180, 67, 63, 236, 65, 66, 162, 71, 152, 191, 76, 77, 78, 79, 80, 82, 83, 103, 92, 98, 118, 85, 100, 89, 116, 114, 115, 104, 99, 111, 86, 97, 122, 251, 90, 238, 193, 254, 95, 252, 130, 235, 233, 102, 121, 123, 105, 106, 107, 108, 110, 217) ORDER BY name
You'll notice that there is no mention of 'locale' or 'en' in this query. If I do a query on SymptomName, including the with_translations method, passing the locale into it, it writes the query exactly as I expect it would.
>> SymptomName.with_translations(I18n.locale).to_sql
=> "SELECT `chief_complaint_names`.* FROM `chief_complaint_names` WHERE `chief_complaint_name_translations`.`locale` = 'en' ORDER BY name"
How can I properly inject the locale into the first query and have it sort my list of Symptoms according to the associated SymptomNames. It is worth noting that I have a method on Symptom that returns the first SymptomName it encounters.
I need this to sort the list by names derived from the locale AND to show the proper names. Any thoughts?
Thank you!
Just a small update for anyone else using this gem. I was surprised to discover that the gem actually creates a new model on the fly for each model that has a translation. This new model has the same name as the original, appended with ::Translation. So SymptomName would have a SymptomName::Translation model. This model has the translated attributes accessible, such as SymptomName::Translation.first.name.
Unfortunately, this new model does not seem to have direct access back to it's namesake as one might expect. There is no SymptomName::Translation.first.symptom`.
Reflecting on all associations does reveal a relationship to said model from the translation model, but the association is not available through the normal chain.
We ended up creating our own model to access this table called SymptomNameTranslation, adding the appropriate associations and joins as a default_scope. Seems to be working great, but would be nice to have this in the gem itself. Might be a good opportunity for a patch to said gem.

Resources