I have a array of string and I want to show in my view all the elements separate by ", ".
When I do a each of this array, How to know if the current element is the last one?
Something like this?
- categoryName.each do |name|
- if (name.last) -- the last elemnt of array
= name
- else
= name + ", "
any idea!
Try to use Array.join
= categoryName.join(', ')
You can simply call categoryName.join(', ') which connects all names in your array with the specified string. It does nothing if only one value is in the array and just returns the single name.
You can use Enumerable#each_with_index for that :
- categoryName.each_with_index do |name, i|
- if (i == categoryName.length - 1) -- the last elemnt of array
= name
- else
= name + ", "
If you are using Rails, you can try this.
categoryName.to_sentence
It will automatically do what you are trying to.
Related
So, I have an array of Genre objects that contain name properties that are Strings that I want a UILabel.text to contain the values.
Id like the label to look something like this ultimately:
String[0] / String[1] / String[2] / String[3]
with the front slashes separating the strings. I can get the label to contain the values, and I can even get the / separators, but my logic is flawed as after the last string I still get the front slash.
Can someone help me with the logic where it will add the string and a front slash unless its the last item in the array and if its the last item in the array it should just add the string and no front slash.
Right now I just have a simple For In loop
if game?.genres?[0].name == nil {
for genre in game!.genres! {
genreLabel.text! += "\(genre.name!) / "
}
}
Ive tried
game.genres.map { $0.name }.joined(separator: “ / “)
but that gives an error of:
Value of type '[Genre]' has no member 'name'
What is the logic here to do something different with the last element?
Because you didn't use enough questions marks, the compiler thinks you're using Optional.map instead of Array.map.
You can get the result you want, like this:
game?.genres?.compactMap(\.name).joined(separator: " / ")
…but you're using too many optionals. Just make genres non-optional. An array can already be empty. It doesn't also need to be nil.
game?.genres.compactMap(\.name).joined(separator: " / ")
…and if you can, make game and name non-optional too.
game.genres.map(\.name).joined(separator: " / ")
You can achieve this by using index
for (index,genre) in game!.genres!.enumerated() {
genreLabel.text! += "\(genre.name!) / "
if index == game!.genres!.count -1 {
// do something with last index
}
}
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
I was wondering whether it is possible to display tables in the console. Something like:
player[1] = {}
player[1].Name = { "Comp_uter15776", "maciozo" }
InputConsole("msg Player names are: " .. player[1].Name)
However, this is obviously wrong as I receive the error about it not being able to concatenate a table value. Is there a workaround for this?
Much thanks in advance!
To turn an array-like table into a string, use table.concat:
InputConsole("msg Player names are: " .. table.concat(player[1].Name, " "))
The second argument is the string placed between each element; it defaults to "".
to make life easier on yourself for this... i'd recommend naming elements in the inner tables as well. this makes the code above easier to read when you need to get at specific values in a table that are meaningful for some purpose.
-- this will return a new instance of a 'player' table each time you call it.
-- if you need to add or remove attributes, you only need to do it in one place.
function getPlayerTable()
return {FirstName = "", LastName = ""}
end
local players = {}
local player = getPlayerTable()
player.FirstName = "Comp_uter15776"
player.LastName = "maciozo"
table.insert(players, player)
... more code to add players ...
local specific_player = players[1]
local specific_playerName = specific_player.FirstName..
" ".. specific_player.LastName
InputConsole("msg Some message ".. specific_playerName)
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).
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.