Ruby custom sorting of first n elements - ruby

I would like to sort an array of string based on my custom ordering. Problem is I dont know all the elements in array but Im sure that it has 3 strings (high/med/low). So I would like those 3 to be first 3 values . Rest at last
Eg:
Incoming arrays
array1 = ["high", "not impt" , "med" , "kind of impt" , "low" ]
array2 = ["low", "rand priority", "med", "high"]
Only high med and low are fixed, rest all keep changing or might not be present at all
required output
["high", "med", "low", rest.(order doesn't matter)]]
I know I can delete and merge, But it will be confusing in code as to why Im doing delete and merge. Any better way?

You can use sort_by method and implement something like this:
["high", "not impt" , "med" , "kind of impt" , "low" ].sort_by do |a|
["high", "med", "low"].index(a) || Float::INFINITY
end
index method returns 0, 1 and 2 for "high", "med" and "low" correspondingly and nil for other values. Thus, "high", "med" and "low" is going to be at the beginning and others at the end since every value is less than Float::INFINITY

Related

Dremel, Null value in repeted field

I have a structure like this (I used JSON to represent data here, but this can be an object in any form):
[
{
"DocID": ["A", "B"]
},
{},
]
Based on Dremel spec, The repetition level for the only data filed here "DocID" (which is repeated) is {0,1,0} and the definition level is {1,1,0} since the last item is null.
Now if I have something like this:
[
{
"DocID": ["A", "B"]
},
{ "DocID": [null]},
]
Then again, the repetition level is {0,1,0} and definition level is {0,1,1}
For storing Dremel data in parquet, we never store null fields (Here)
So we store two value "A", "B" in this case (encoding doesn't matter), but for constructing the structure, the first RLevel is zero, so this is start of a new object, the first DLevel is 1, so this is not null. we read the first value, which is "A" (Correct), the second RLevel is 1 it means it is still the same object and it is a repeated field, the DLevel is 1 so it is not null, we read the second value which is "B" (Correct).
The third RLevel is 0, this means a new object. In the first example, the DLevel is zero, so it is null, we don't need to read anything (there is nothing left) and it works.
But in the second case, the DLevel is 1, so we need to read something, and there is nothing left to read.
What we should do in this case?
Just for context, I am co-author of fraugster/parquet-go library, and this is the issue we faced recently.

use ruby to identify if array of terms contains one and ONLY one repeated term

In categorical logic, categorical syllogism requires four terms, but can only have three unique terms; that is one and ONLY one term can and must be repeated.
I trying to write a Ruby test for this but having a little bit of trouble.
Consider the following three arrays of terms, the first of which is a valid list, while the other two are invalid (termarray2 contains 4 unique terms and termarray3 contains only 2 unique terms).
termarray1 = ["Dogs", "Mortal Things", "Mortal Things", "Living Things"]
termarray2 = ["Dogs", "Mortal Things", "Cats", "Living Things"]
termarray3 = ["Dogs", "Mortal Things", "Mortal Things", "Dogs"]
I want to write a test called three_terms?
It should return true for termarray1 and false for termarray2 and termarray3
Any ideas how I could to this?
The uniq methods returns the unique elements in an array.
This should work:
array.uniq.count == 3
But the test you mention also checks that the original array has four elements. Thus the entire check should be:
(array.count == 4) && (array.uniq.count == 3)
Check to see if the size of unique elements is 3 (using Array#uniq):
array.uniq.size == 3
You could also monkey-patch Array with three_terms?:
class Array
def three_terms?
uniq.size == 3
end
end

ruby return fixed number of elements in a certain order

This might be a dumb question.
I know sample returns random number of elements from an array.
For example,
[1,2,3].sample.times do
Is there a way to return a fixed number of elements in a certain order always?
I dont know how to do this in ruby.
EDIT:
Lets say I always want to return penalty_name, severity and name only from the second and last array here always.:
offenses = PERSON_SUMMARY[:offenses].map do |offense|
offense[:penalties].map do |penalty|
penalty.merge(name: offense[:offense_name])
end
end.flatten
=> [{:penalty_name=>"Prison", :severity=>"Medium", :name=>"Speeding"}, {:penalty_name=>"Ticket", :severity=>"Low", :name=>"Speeding"}, {:penalty_name=>"Prison", :severity=>"Medium", :name=>"Shoplifting"}, {:penalty_name=>"Fine", :severity=>"Low", :name=>"Shoplifting"}]
right now I am doing:
offenses.each do |hash|
hash.sample
I think you want something like:
[1,2,3,4,5].sample(4).sort
It will take 4 random number from the array and order it...
edit - after your comment:
[5,4,3,2,1].values_at(1,-1).sort #second element(1) and last one(-1)
=>[1, 4]
You can specify which ones you want with values_at (negative numbers count from the back.)
ar=[{:penalty_name=>"Prison", :severity=>"Medium", :name=>"Speeding"}, {:penalty_name=>"Ticket", :severity=>"Low", :name=>"Speeding"}, {:penalty_name=>"Prison", :severity=>"Medium", :name=>"Shoplifting"}, {:penalty_name=>"Fine", :severity=>"Low", :name=>"Shoplifting"}]
p ar.values_at(1,-1).map{|h|h.values}
#=> [["Ticket", "Low", "Speeding"], ["Fine", "Low", "Shoplifting"]]

how to find best matching element in array of numbers?

I need help with something that seems simple but confuses me. Trying to write some fuzzy matching method that copes with differences in format between what value is computed as needed, and which are actually available from a selection list.
The value (option strike price) is always a computed Float like 85.0 or Int.
The array contains numbers in string form, unpredictable in either increment or whether they will be shown rounded to some decimal (including extra zeros like 5.50) or no decimal (like 85), eg.:
select_list = ["77.5", "80", "82.5", "85", "87.5", "90", "95", "100", "105"]
I am unsure how to write a simple line or two of code that will return the closest matching element (by number value) as it appears in the array. For example, if select_list.contains? 85.0 returned "85"
Actually, the selection choices come from a Watir::Webdriver browser.select_list(:id, "lstStrike0_1") HTML object whose visible text (not HTML value) are those numbers; maybe there is a more direct way to just call browser.select_list(:id, "lstStrike0_1").select X without having to figure out in Watir how to convert all those choices into a Ruby array?
xs = ["77.5", "80", "82.5", "85", "87.5", "90", "95", "100", "105"]
xs.min_by { |x| (x.to_f - 82.4).abs }
#=> "82.5"
I'm not a ruby coder so this might not be the best way to do it
def select_closest(list, target)
return (list.map {|x| [(x.to_f - target).abs, x]}).min[1]
end
select_list = ["77.5", "80", "82.5", "85", "87.5", "90", "95", "100", "105"]
puts select_closest(select_list, 81) # yields 80

Ruby: Ordered array and add to array at next number if number exists

I was wondering if anyone new of an easy way to organize an array by numbers but if the number already exists push it to the next number that doesn't exist I was thinking of just creating a multi-dimensional ordered array where if numbers clash (such as 2 pages having 1) then the first would be [1][1] and the second would be [1][2] but is there a better way to handle this?
Edit; an example:
page1 -> sets order to 1
page2 -> sets order to 1
page3 -> sets order to 2
Normally I would go through and YAML read the pages configurations and get the order and then use that number and set _site.sidebar[_config["order"]] but in this case it would clash and it wouldn't add it. So I'm looking for a way to allow for user mistakes but preserve order keeping the first found as one but if one exists shift the array down and put the second 1 as two.
This sounds like you're implementing a hashtable, and using 'number' as hash. There are all kinds of algorithms for that, just look for hashtable algorithms.
Here is the final snippet on how I implemented what I was asking about, just in case somebody else stumbles upon this thread looking for the same sort of thing. I basically just wanted to preserve the order, in my actual application of the code I used a normal multi-dimensional array since "order" was pulled from YAML front so it is it's own variable.
data = []
demo = {
"page_1" => {
"order" => 1,
"data" => "Hello World 1"
},
"page_2" => {
"order" => 2,
"data" => "Hello World 2"
},
"page_3" => {
"order" => 1,
"data" => "Hello World 3"
},
"page_4" => {
"order" => "a",
"data" => "Hello World 4"
}
}
demo.each |key, page|
local_data = page["data"]
order = page["order"].to_i.floor
data[order] ||= []
data[order] << local_data
}
puts data.flatten.join(" ").strip

Resources