Convert string arra into array of integers - ruby-on-rails

I am receiving params like this "[[201], [511], [3451]]", I want to convert it into [201, 511, 3451]

Let's say params is what you're receiving, you can use scan and map to use a Regular Expression, look for the digits in the response and then map each item in the array to an integer:
params = "[[201], [511], [3451]]"
params_array = params.scan(/\d+/).map(&:to_i)
What we are doing here is we are looking through the string and selecting only the digits with the Scan method, afterwards we get a string array so to convert it into integers we use the Map method. As per the map method, thanks to Cary Swoveland for the update on it.

It will help you!
str_arr = "[[201], [511], [3451]]"
JSON.parse(str_arr).flatten
or
eval(str_arr).flatten

here is an interesting way (note that it only works in case your params is an array string)
arr1 = instance_eval("[1,2,3]")
puts arr1.inspect # [1,2,3]
arr2 = instance_eval("[[201], [511], [3451]]")
puts arr2.inspect # [[201], [511], [3451]]

First, I would make a sanity check that you don't get malevolent code injected:
raise "Can not convert #{params}" if /[^\[\]\d]/ =~ params
Now you can assert that your string is safe:
params.untaint
and then convert
arr = eval(params).flatten
or
arr = eval(params).flatten(1)
depending on what exactly you want to receive if you have deeply-nested "arrays" in your string.

Related

Why does ruby sort Alphanumeric strings with 1's and 0's as binary?

I have an array ["Q10", "Q100", "Q1000", "Q1000a", "Q1001", "Q98"]. After sorting it, I get the following result:
['Q100', 'Q1000', 'Q1000a','Q98', 'Q10', 'Q1001'].sort
["Q10", "Q100", "Q1000", "Q1000a", "Q1001", "Q98"]
Because of this behaviour, I cannot sort my ActiveRecord objects correctly. I have a model of Question which has a field label. I need to sort it based on label. So Question with label Q1 would be first and the question with label Q1a would follow and so on. I get in a similar order with ActiveRecord described to the above example of array. I am using postgresql as my database.
Now I have 3 questions.
Why alphanumeric string sorting behave that way?
How can I achieve my required sorting without using the sort block?
How can I achieve that sorting in ActiveRecord?
If your array were
arr = ["Q10", "Q100", "Q1000", "Q8", "Q1001", "Q98"]
you could write
arr.sort_by { |s| s[/\d+/].to_i }
#=> ["Q8", "Q10", "Q98", "Q100", "Q1000", "Q1001"]
If
s = "Q1000"
then
s[/\d+/].to_i
#=> 1000
See Enumerable#sort_by and String#[].
The regular expression /\d+/ matches a substring of s that contains one or more digits.
If the array were
arr = ["Q10b", "Q100", "Q1000", "Q10a", "Q1001", "Q98", "Q10c"]
you could write
arr.sort_by { |s| [s[/\d+/].to_i, s[/\D+\z/]] }
#=> ["Q10a", "Q10b", "Q10c", "Q98", "Q100", "Q1000", "Q1001"]
If
s = "Q10b"
then
[s[/\d+/].to_i, s[/\D+\z/]]
#=> [10, "b"]
The regular expression /\D+\z/ matches a substring of s that contains one or more non-digits at the end (\z) of the string.
See Array#<=>, specifically the third paragraph, for an explanation of how arrays are ordered when sorting.
If the array were
arr = ["Q10b", "P100", "Q1000", "PQ10a", "Q1001", "Q98", "Q10c"]
you could write
arr.sort_by { |s| [s[/\A\D+/], s[/\d+/].to_i, s[/\D+\z/]] }
#=> ["P100", "PQ10a", "Q10b", "Q10c", "Q98", "Q1000", "Q1001"]
If
s = "PQ10a"
then
[s[/\A\D+/], s[/\d+/].to_i, s[/\D+\z/]]
#=> ["PQ", 10, "a"]
The regular expression /\A\D+/ matches a substring of s that contains one or more non-digits at the beginning (\A) of the string.
This should do the trick for you, casting them to numbers before sorting.
['100', '1000', '98', '10', '1001'].map(&:to_i).sort
This strange map(&:to_i) is shorthand for map { |x| x.to_i }
Edit:
You could do this with AR. This will throw an error if the column doesn't contain a number disguised as a string.
Model.order("some_column::integer")
Edit II:
Try this if it contains strings as well.
Model.order("cast(some_column as integer))"

How to compute each cell of a line in an array with Ruby on Rails 5.2?

While importing from an excel file to a database, I need to format a hierarchy so it appears with leading zeros:
10.1.1.4 must be transformed into 1.010.001.001.004
I tried to iterate through and concatenate the elements:
record.hierarchy = spreadsheet.cell(i,2).split('.').each do |t|
index = index || '1.'
index = index + '.' + (((t.to_i + 1000).to_s).last(3))
end
which actually returns and array of ["10", "1", "1", "4"], not computed. I would expect this to return the last evaluated value: index
I tried to compute it directly inside the array:
record.hierarchy = '1.' + (((spreadsheet.cell(i,2).split('.').each).to_i + 1000).to_s).last(3).join('.')
which raises an undefined method to_i for enumerator.
Can someone explain me how to structure and solve this computation?
Thanks
Use #rjust.
'10.1.1.4'.split('.').map { |l| l.rjust(3, '0') }.join('.')
Your first solution uses assignment with #each. #each will not return modified array.
It is not necessary to convert the string to an array, modify the elements of the array and then join the array back into a string. The string can be modified directly using String#gsub.
str = '10.1.1.4'
('1.' + str).gsub(/(?<=\.)\d+/) { |s| sprintf("%03d", s.to_i) }
#=> "1.010.001.001.004"
See Kernel#sprintf.
(?<=\.) is positive lookbehind that requires the matched digits to be preceded by a period. I've assumed the string is known to contain between one and three digits before and after each period.
You can try different function for leading zeroes and inject to not set default value inside the loop
record.hierarchy = spreadsheet.cell(i,2).split('.').inject('1') do |result, t|
result + '.' + t.rjust(3, '0')
end

Convert string to XXX

In queel, I can do:
User.where{id.eq "2" | admin.eq true}
to query. I want to know if I can trans [sic] a string, which is a condition like:
string = "id: 2"
or
string = "id.eq '2' | admin.eq true"
and run:
User.where{string}
or
User.where(string)
The result is not a hash. How can I do that?
What you need is hash and not string. You can use it like this,
hash = {id: 2}
User.where(hash)
And if you really want to use string you can do it like this,
string = "id = 2"
User.where(string)
If you want to execute the string you can use eval. In that case it will be like this,
string = "{id: 2}"
User.where(eval string)
NB to use the variant below, one should read first five pages of Google output on query “ruby eval is evil.”
User.where(instance_eval("{#{string}}"))

Ruby array, convert to two arrays in my case

I have an array of string which contains the "firstname.lastname?some.xx" format strings:
customers = ["aaa.bbb?q21.dd", "ccc.ddd?ew3.yt", "www.uuu?nbg.xcv", ...]
Now, I would like to use this array to produce two arrays, with:
the element of the 1st array has only the string before "?" and replace the "." to a space.
the element of the 2nd array is the string after "?" and include "?"
That's I want to produce the following two arrays from the customers array:
1st_arr = ["aaa bbb", "ccc ddd", "www uuu", ...]
2nd_arr = ["?q21.dd", "?ew3.yt", "?nbg.xcv", ...]
What is the most efficient way to do it if I use customers array as an argument of a method?
def produce_two_arr customers
#What is the most efficient way to produce the two arrays
#What I did:
1st_arr = Array.new
2nd_arr = Array.new
customers.each do |el|
1st_Str, 2nd_Str=el.split('?')
1st_arr << 1st_str.gsub(/\./, " ")
2nd_arr << "?"+2nd_str
end
p 1st_arr
p 2nd_arr
end
Functional approach: when you are generating results inside a loop but you want them to be split in different arrays, Array#transpose comes handy:
ary1, ary2 = customers.map do |customer|
a, b = customer.split("?", 2)
[a.gsub(".", " "), "?" + b]
end.transpose
Anytime you're building an array from another, reduce (a.k.a. inject) is a great help:
But sometimes, a good ol' map is all you need (in this case, either one works because you're building an array of the same size):
a, b = customers.map do |customer|
a, b = customer.split('?')
[a.tr('.', ' '), "?#{b}"]
end.transpose
This is very efficient since you're only iterating through customers a single time and you are making efficient use of memory by not creating lots of extraneous strings and arrays through the + method.
Array#collect is good for this type of thing:
arr1 = customers.collect{ |c| c.split("?").first.sub( ".", "" ) }
arr2 = customers.collect{ |c| "?" + c.split("?").last }
But, you have to do the initial c.split("?") twice. So, it's effecient from an amount of code point of view, but more CPU intensive.
1st_arr = customers.collect{ |name| name.gsub(/\?.*\z/,'').gsub(/\./,' ') }
2nd_arr = customers.collect{ |name| name.match(/\?.*\z/)[0] }
array1, array2 = customers.map{|el| el.sub('.', ' ').split /(?:\?)/}.transpose
Based on #Tokland 's code, but it avoids the extra variables (by using 'sub' instead of 'gsub') and the re-attaching of '?' (by using a non-capturing regex).

help with oauthService and linkedin

I am trying to iterate over a list of parameters, in a grails controller. when I have a list, longer than one element, like this:
[D4L2DYJlSw, 8OXQWKDDvX]
the following code works fine:
def recipientId = params.email
recipientId.each { test->
System.print(test + "\n")
}
The output being:
A4L2DYJlSw
8OXQWKDDvX
But, if the list only has one item, the output is not the only item, but each letter in the list. for example, if my params list is :
A4L2DYJlSwD
using the same code as above, the output becomes:
A
4
L
2
D
Y
J
l
S
w
can anyone tell me what's going on and what I am doing wrong?
thanks
jason
I run at the same problem a while ago! My solution for that it was
def gameId = params.gameId
def selectedGameList = gameId.class.isArray() ? Game.getAll(gameId as List) : Game.get(gameId);
because in my case I was getting 1 or more game Ids as parameters!
What you can do is the same:
def recipientId = params.email
if(recipientId.class.isArray()){
// smtg
}else{
// smtg
}
Because what is happening here is, as soon as you call '.each' groovy transform that object in a list! and 'String AS LIST' in groovy means char_array of that string!
My guess would be (from what I've seen with groovy elsewhere) is that it is trying to figure out what the type for recipientId should be since you haven't given it one (and it's thus dynamic).
In your first example, groovy decided what got passed to the .each{} closure was a List<String>. The second example, as there is only one String, groovy decides the type should be String and .each{} knows how to iterate over a String too - it just converts it to a char[].
You could simply make recipientId a List<String> I think in this case.
You can also try like this:
def recipientId = params.email instanceof List ? params.email : [params.email]
recipientId.each { test-> System.print(test + "\n") }
It will handle both the cases ..
Grails provides a built-in way to guarantee that a specific parameter is a list, even when only one was submitted. This is actually the preferred way to get a list of items when the number of items may be 0, 1, or more:
def recipientId = params.list("email")
recipientId.each { test->
System.print(test + "\n")
}
The params object will wrap a single item as a list, or return the list if there is more than one.

Resources