Lua table.sort issues - sorting

So, having some issues getting this table to sort correctly.
Basically, table.sort thinks 10 == 1, 20 == 2, and so on. I'll post my sort code below, but I'm not sure if that has anything to do with it. Is this just an inherent issue with the table.sort algorithm in Lua?
if svKey == "q" and metalMatch == true then
table.sort(vSort.metals, function(oneMQ, twoMQ)
return oneMQ.metalQ > twoMQ.metalQ
end)
end
Values stored in vSort.metals.metalQ are strings anywhere from 1 to 3 digits long. Is there a way to make table.sort differentiate between single-, double-, and triple-digit values?

The order operators work as follows. If both arguments are numbers, then they are compared as such. Otherwise, if both arguments are strings, then their values are compared according to the current locale. You can set the locale. Strings are compared lexicographically, which is generally character by character with shorter strings before longer strings.
If you want a numeric sort, then use, well, a numeric type. This might work:
function(oneMQ, twoMQ)
return tonumber(oneMQ.metalQ) > tonumber(twoMQ.metalQ)
end
It assumes that all the metalQ values are numeric. If not, coerce to a default or provide a fallback sort order in your sort expression for non-numeric values.

Related

How to call Lua table value explicitly when using integer counter (i,j,k) in a for loop to make the table name/address?

I have to be honest that I don't quite understand Lua that well yet. I am trying to overwrite a local numeric value assigned to a set table address (is this the right term?).
The addresses are of the type:
project.models.stor1.inputs.T_in.default, project.models.stor2.inputs.T_in.default and so on with the stor number increasing.
I would like to do this in a for loop but cannot find the right expression to make the entire string be accepted by Lua as a table address (again, I hope this is the right term).
So far, I tried the following to concatenate the strings but without success in calling and then overwriting the value:
for k = 1,10,1 do
project.models.["stor"..k].inputs.T_in.default = 25
end
for k = 1,10,1 do
"project.models.stor"..j..".T_in.default" = 25
end
EDIT:
I think I found the solution as per https://www.lua.org/pil/2.5.html:
A common mistake for beginners is to confuse a.x with a[x]. The first form represents a["x"], that is, a table indexed by the string "x". The second form is a table indexed by the value of the variable x. See the difference:
for k = 1,10,1 do
project["models"]["stor"..k]["inputs"]["T_in"]["default"] = 25
end
You were almost close.
Lua supports this representation by providing a.name as syntactic sugar for a["name"].
Read more: https://www.lua.org/pil/2.5.html
You can use only one syntax in time.
Either tbl.key or tbl["key"].
The limitation of . is that you can only use constant strings in it (which are also valid variable names).
In square brackets [] you can evaluate runtime expressions.
Correct way to do it:
project.models["stor"..k].inputs.T_in.default = 25
The . in models.["stor"..k] is unnecessary and causes an error. The correct syntax is just models["stor"..k].

Check if a value is less than any value of an array

I have a time-stamp '2016-02-08 16:23:53' and an array containing different time-stamps ['2016-02-09 14:23:53', '2015-02-08 16:23:53', '2016-02-08 16:22:53'].
What is the simplest way to check if the value is less than any of the value in array and return true or false?
If timestamps are strings:
['2016-02-09 14:23:53', '2015-02-08 16:23:53', '2016-02-08 16:22:53'].none? do |ts|
Time.parse(ts) > Time.parse('2016-02-08 16:23:53')
end
If timestamps are already instances of Time, there is no need for parse:
timestamps_array.none? do |ts|
ts > Time.parse('2016-02-08 16:23:53')
end
Well, for the sake of #sawa’s patience :)
It is inefficient to parse the value to compare against each time in the loop:
timestamp_to_check = Time.parse('2016-02-08 16:23:53')
['2016-02-09 14:23:53', '2015-02-08 16:23:53', '2016-02-08 16:22:53'].none? do |ts|
Time.parse(ts) > timestamp_to_check
end
Checking if a value is less than any value in an array may be accomplished in Ruby with
value < array_of_values.min
The simplest way to solve the example given by the OP is
'2016-02-08 16:23:53' < ['2016-02-09 14:23:53',
'2015-02-08 16:23:53',
'2016-02-08 16:22:53'].min
Comparing string representations of dates and times will only work if all strings involved use the same format, the numbers are ordered from big to small (years down to seconds), and the numbers use leading zeros.
The use of adequate data types (like Time or DateTime) instead of strings for storing and processing dates and times will improve the robustness of the software.

Max function not returning the max value in an array

I have unsorted arrays as some_array. When I use some_array.max to get the max number, I get the following output. For:
some_array = ["1.10.0", "1.11.0", "1.12.0", "1.13.0", "1.14.0", "1.15.0", "1.16.0",
"1.16.1", "1.17.0", "1.18.0", "1.7.0", "1.8.0"]
I get 1.8.0 instead of 1.18.0. For:
some_array = ["1.11.0", "1.12.0", "1.13.0", "1.14.0", "1.14.1", "1.15.0", "1.16.0", "1.17.0", "1.18.0", "1.19.0", "1.5.0", "1.8.0", "1.9.0"]
I get 1.9.0 instead of 1.19.0.
To me, it looks like max is picking up the last value from the array. Shouldn't max print the maximum value in the array? Do I have to sort the array before using max? Is there any other way to get max value out of array?
The existing answers already point out the problem in your code. This is how you may write it:
some_array.max_by { |version| version.split(".").map(&:to_i) }
#=> "1.18.0"
The problem is that you are thinking that they are integers, but they're not - they're strings. So they get sorted lexicographically. That is to say, treat them like an alphabet. "1.8.0" is greater than "1.18.0", because the "8" is greater than the "1" character (in the 3rd position).
Look at the following random strings. They are in order:
"abcdef"
"abdghi"
"adaaaa"
Why are they in order? Because you look at the first character, and compare them. Then look at the next character, and compare them, etc. Now look at your example:
"1.18.0"
"1.8.0"
Look at the first character of each, it's a "1", it's equal. Look at the next character, both are ".", they're equal. Look at the next characters, "1" and "8". "1" comes before "8". Therefore, "1.8.0" must come lexicographically after "1.18.0".
If you want to treat them like integers, there are a few things you can do. 1) You can write your own sort method in a block, or 2) wrap the strings in some hand-made Version object, and then write the comparator there.
If you need help with these specific ideas, let us know.
Shouldn't max() print the maximum value in the array?
Yes. It should. And it is doing so, as you can see in the results you got.
Do I have to sort the array before using max()?
No.
Is there any other way to get max value out of array?
Yes. But you already got the max value.
this method checks character by character.
So, comparing 1.9.0 and 1.19.0 , it will give 1.9.0 as bigger than 1.19.0 because the part "1." is equal in both strings, but the next character is 9 in the first one, and 1 in the second one, so the method will say 1.9.0 is bigger since 9 comes later than 1 in ASCII code.
When you need to sort version numbers the Versionomy Gem helps a lot. It also handles all the edge cases, for example beta and RC versions: 1.8beta < 1.8RC < 1.8.
Using that gem you must change your code to something like this:
require 'versionomy'
some_array.max_by { |version| Versionomy.parse(version) }

Count, size, length...too many choices in Ruby?

I can't seem to find a definitive answer on this and I want to make sure I understand this to the "n'th level" :-)
a = { "a" => "Hello", "b" => "World" }
a.count # 2
a.size # 2
a.length # 2
a = [ 10, 20 ]
a.count # 2
a.size # 2
a.length # 2
So which to use? If I want to know if a has more than one element then it doesn't seem to matter but I want to make sure I understand the real difference. This applies to arrays too. I get the same results.
Also, I realize that count/size/length have different meanings with ActiveRecord. I'm mostly interested in pure Ruby (1.92) right now but if anyone wants to chime in on the difference AR makes that would be appreciated as well.
Thanks!
For arrays and hashes size is an alias for length. They are synonyms and do exactly the same thing.
count is more versatile - it can take an element or predicate and count only those items that match.
> [1,2,3].count{|x| x > 2 }
=> 1
In the case where you don't provide a parameter to count it has basically the same effect as calling length. There can be a performance difference though.
We can see from the source code for Array that they do almost exactly the same thing. Here is the C code for the implementation of array.length:
static VALUE
rb_ary_length(VALUE ary)
{
long len = RARRAY_LEN(ary);
return LONG2NUM(len);
}
And here is the relevant part from the implementation of array.count:
static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
long n = 0;
if (argc == 0) {
VALUE *p, *pend;
if (!rb_block_given_p())
return LONG2NUM(RARRAY_LEN(ary));
// etc..
}
}
The code for array.count does a few extra checks but in the end calls the exact same code: LONG2NUM(RARRAY_LEN(ary)).
Hashes (source code) on the other hand don't seem to implement their own optimized version of count so the implementation from Enumerable (source code) is used, which iterates over all the elements and counts them one-by-one.
In general I'd advise using length (or its alias size) rather than count if you want to know how many elements there are altogether.
Regarding ActiveRecord, on the other hand, there are important differences. check out this post:
Counting ActiveRecord associations: count, size or length?
There is a crucial difference for applications which make use of database connections.
When you are using many ORMs (ActiveRecord, DataMapper, etc.) the general understanding is that .size will generate a query that requests all of the items from the database ('select * from mytable') and then give you the number of items resulting, whereas .count will generate a single query ('select count(*) from mytable') which is considerably faster.
Because these ORMs are so prevalent I following the principle of least astonishment. In general if I have something in memory already, then I use .size, and if my code will generate a request to a database (or external service via an API) I use .count.
In most cases (e.g. Array or String) size is an alias for length.
count normally comes from Enumerable and can take an optional predicate block. Thus enumerable.count {cond} is [roughly] (enumerable.select {cond}).length -- it can of course bypass the intermediate structure as it just needs the count of matching predicates.
Note: I am not sure if count forces an evaluation of the enumeration if the block is not specified or if it short-circuits to the length if possible.
Edit (and thanks to Mark's answer!): count without a block (at least for Arrays) does not force an evaluation. I suppose without formal behavior it's "open" for other implementations, if forcing an evaluation without a predicate ever even really makes sense anyway.
I found a good answare at http://blog.hasmanythrough.com/2008/2/27/count-length-size
In ActiveRecord, there are several ways to find out how many records
are in an association, and there are some subtle differences in how
they work.
post.comments.count - Determine the number of elements with an SQL
COUNT query. You can also specify conditions to count only a subset of
the associated elements (e.g. :conditions => {:author_name =>
"josh"}). If you set up a counter cache on the association, #count
will return that cached value instead of executing a new query.
post.comments.length - This always loads the contents of the
association into memory, then returns the number of elements loaded.
Note that this won't force an update if the association had been
previously loaded and then new comments were created through another
way (e.g. Comment.create(...) instead of post.comments.create(...)).
post.comments.size - This works as a combination of the two previous
options. If the collection has already been loaded, it will return its
length just like calling #length. If it hasn't been loaded yet, it's
like calling #count.
Also I have a personal experience:
<%= h(params.size.to_s) %> # works_like_that !
<%= h(params.count.to_s) %> # does_not_work_like_that !
We have a several ways to find out how many elements in an array like .length, .count and .size. However, It's better to use array.size rather than array.count. Because .size is better in performance.
Adding more to Mark Byers answer. In Ruby the method array.size is an alias to Array#length method. There is no technical difference in using any of these two methods. Possibly you won't see any difference in performance as well. However, the array.count also does the same job but with some extra functionalities Array#count
It can be used to get total no of elements based on some condition. Count can be called in three ways:
Array#count # Returns number of elements in Array
Array#count n # Returns number of elements having value n in Array
Array#count{|i| i.even?} Returns count based on condition invoked on each element array
array = [1,2,3,4,5,6,7,4,3,2,4,5,6,7,1,2,4]
array.size # => 17
array.length # => 17
array.count # => 17
Here all three methods do the same job. However here is where the count gets interesting.
Let us say, I want to find how many array elements does the array contains with value 2
array.count 2 # => 3
The array has a total of three elements with value as 2.
Now, I want to find all the array elements greater than 4
array.count{|i| i > 4} # =>6
The array has total 6 elements which are > than 4.
I hope it gives some info about count method.

Sorting strings containing numbers in a user friendly way

Being used to the standard way of sorting strings, I was surprised when I noticed that Windows sorts files by their names in a kind of advanced way. Let me give you an example:
Track1.mp3
Track2.mp3
Track10.mp3
Track20.mp3
I think that those names are compared (during sorting) based on letters and by numbers separately.
On the other hand, the following is the same list sorted in a standard way:
Track1.mp3
Track10.mp3
Track2.mp3
Track20.mp3
I would like to create a comparing alogorithm in Delphi that would let me sort strings in the same way. At first I thought it would be enough to compare consecutive characters of two strings while they are letters. When a digit would be found at some position of both the strings, I would read all digits following them to form a number and then compare the numbers.
To give you an example, I'll compare "Track10" and "Track2" strings this way:
1) read characters while they are equal and while they are letters: "Track", "Track"
2) if a digit is found, read all following digits: "10", "2"
2a) if they are equal, go to 1 or else finish
Ten is greater than two, so "Track10" is greater than "Track2"
It had seemed that everything would be all right until I noticed, during my tests, that Windows considered "Track010" lower than "Track10", while I thought the first one was greater as it was longer (not mentioning that according to my algorithm both the strings would be equal, which is wrong).
Could you provide me with the idea how exactly Windows sorts files by names or maybe you have a ready-to-use algorithm (in any programming language) that I could base on?
Thanks a lot!
Mariusz
Jeff wrote up an article about this on Coding Horror. This is called natural sorting, where you effectively treat a group of digits as a single "character". There are implementations out there in every language under the sun, but strangely it's not usually built-in to most languages' standard libraries.
The mother of all sorts:
ls '*.mp3' | sort --version-sort
The absolute easiest way, I found, was isolate the string you want, so in the OP's case, Path.GetFileNameWithoutExtension(), remove the non-digits, convert to int, and sort. Using LINQ and some extension methods, it's a one-liner. In my case, I was going on directories:
Directory.GetDirectories(#"a:\b\c").OrderBy(x => x.RemoveNonDigits().ToIntOrZero())
Where RemoveNonDigits and ToIntOrZero are extensions methods:
public static string RemoveNonDigits(this string value) {
return Regex.Replace(value, "[^0-9]", string.Empty);
}
public static int ToIntOrZero(this string toConvert) {
try {
if (toConvert == null || toConvert.Trim() == string.Empty) return 0;
return int.Parse(toConvert);
} catch (Exception) {
return 0;
}
}
The extension methods are common tools I use everywhere. YMMV.
Here's a Python approach:
import re
def tryint(s):
"""
Return an int if possible, or `s` unchanged.
"""
try:
return int(s)
except ValueError:
return s
def alphanum_key(s):
"""
Turn a string into a list of string and number chunks.
>>> alphanum_key("z23a")
["z", 23, "a"]
"""
return [ tryint(c) for c in re.split('([0-9]+)', s) ]
def human_sort(l):
"""
Sort a list in the way that humans expect.
"""
l.sort(key=alphanum_key)
And a blog post with more detail: https://nedbatchelder.com/blog/200712/human_sorting.html

Resources