How to add Array with hint and hint_string? - godot

based on this answer it is possible to create export variables via _get_property_list() like this:
var _properties := {
"x": "",
"y": ""
}
func _get_property_list() -> Array:
if not Engine.editor_hint or not is_inside_tree():
return []
var result := []
for property_name in _properties.keys():
result.append(
{
name = property_name,
type = typeof(_properties[property_name]),
usage = PROPERTY_USAGE_DEFAULT
}
)
return result
...
but what if I wanted to add an array with hint and hint_string?
the equivalent of export(Array,float,0, 100,10) var Multiples=[0,0,0,0]
result.append(
{
name = property_name,
type = typeof(Array),
usage = PROPERTY_USAGE_DEFAULT,
hint=???,
hint_string=???
}

Scroll to the end where it says "To put it all together" if you just want the rules.
To begin with, I'll be using code that looks like this:
tool
extends Node
var backing_field
func _set(property:String, value) -> bool:
if property == "property_name":
backing_field = value
return true
return false
func _get(property:String):
if property == "property_name":
return backing_field
return null
Pretend that code is always there. So we will be specifying this property "property_name" in _get_property_list.
For reference, I'll start by showing how _get_property_list works in other cases. For example we can make the property an int:
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_INT,
usage = PROPERTY_USAGE_DEFAULT,
}
]
And we can use hint and hint_string to further specify how it will behave in the inspector panel. For instance we can narrow the property to an specific range like this:
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_INT,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_RANGE,
hint_string = "0,10"
}
]
And that would give the property a range from 0 to 10.
We can of course specify that the property is an Array instead of an int:
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
}
]
And here is where we get to the question: What hint and hint_string can we use with TYPE_ARRAY.
If we have a look at GDScript exports we find out that we can export an array and also specify the type of the elements, like this:
export(Array, int) var my_array = [1, 2, 3]
So, presumably we will be able to do that with _get_property_list instead (Without resourcing to Pool*Array types - we will not be able to specify ranges with those anyway). How do we do that? I'll show you.
From here on, these is all undocumented. Most of this I figured this out by experimentation. By the way, for Godot 4.0 this will be different.
To specify the type of the elements of the array:
The hint must be 26 in Godot 3.5. This is an undocumented constant I found by experimentation, and I
found the name in Godot source: PROPERTY_HINT_TYPE_STRING, however
it is not exposed to GDScript. These are the values for other versions of Godot:
Godot 3.0: PROPERTY_HINT_TYPE_STRING = 23
Godot 3.1 to 3.4: PROPERTY_HINT_TYPE_STRING = 24
Godot 3.5: PROPERTY_HINT_TYPE_STRING = 26
To avoid the issue, I'll declare it as PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2.
The hint_string must be the TYPE_* constant converted to String with ":" appended at the end.
For example, if the type of the elements is int, you put "2:" in the hint_string. If the type of the elements is float, you put "3:" on the hint_string. Like this:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = str(TYPE_INT) + ":"
}
]
For reference str(TYPE_INT) is "2", so str(TYPE_INT) + ":" is "2:".
Ok, but what if we want to specify more about the elements? For example what if we want to say that we have an Array of int in the range from 0 to 10 using _get_property_list?
In that case the hint_string will be:
The the TYPE_* constant converted to String
Followed by "/"
Followed by the PROPERTY_HINT_* constant converted to String
Followed by ":"
Followed by the hint_string you would use with that PROPERTY_HINT_* constant.
Like this:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = str(TYPE_INT) + "/" + str(PROPERTY_HINT_RANGE) + ":0,10"
}
]
Here the hint_string comes up as "2/1:0,10". Notice that the "2" is now followed by "/" instead of ":".
Alright, that begs the question. What if the elements of the Array must also be Arrays? Well, we can go back to having an Array and specifying the type, like this:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = str(TYPE_ARRAY) + ":"
}
]
Here the hint_string comes up as "19:. That the "19" came from str(TYPE_ARRAY), and I'm highlighting that because I'll start using writing "19" instead of str(TYPE_ARRAY) in the code.
Well, Let us say we want to specify the type of the elements of the arrays that are elements of the array. For example, let us say that we want an array of arrays of int. That would be like this:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = "19:" + str(TYPE_INT) + ":"
}
]
Here the hint_string comes up as "19:2:.
And we can put more "19:" to make an Array of Arrays of Arrays of whatever and so on. So this is an Array of Arrays of Arrays of ints:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = "19:19:" + str(TYPE_INT) + ":"
}
]
Here the hint_string comes up as "19:19:2:.
Now, let us say that you want to int in the range from 0 to 1 as before, we would have to do this:
const PROPERTY_HINT_TYPE_STRING := PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2
func _get_property_list() -> Array:
return [
{
name = "property_name",
type = TYPE_ARRAY,
usage = PROPERTY_USAGE_DEFAULT,
hint = PROPERTY_HINT_TYPE_STRING,
hint_string = "19:19:" + str(TYPE_INT) + "/" + str(PROPERTY_HINT_RANGE) + ":0,10"
}
]
Here the hint_string comes up as "19:19:2/1:0,10.
Notice (again) that we don't have "19:19:" + str(TYPE_INT) followed by ":" but by "/".
To put it all together:
The type must be TYPE_ARRAY (Which is 19).
The hint must be 26 for Godot 3.5 (this is an undocumented constant called PROPERTY_HINT_TYPE_STRING, historically it has been PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2).
The hint_string must be:
"19:" for each level of nested Arrays we want. None if the Array is not meant to have other Arrays inside.
Followed by the TYPE_* constant that represents the type of the elements.
Then by either:
To specify what would be the hint_string for the elements:
"/"
Followed by the PROPERTY_HINT_* constant converted to String
Followed by ":"
Followed by the hint_string you would use with that PROPERTY_HINT_* constant.
Otherwise:
":"
The other attributes are not affected by this being an array. You can set them as if you were making a property of the type of the elements of the array.
These are some examples of GDScript exports translated to hint_string (remember to set type to TYPE_ARRAY and hint to 26 for Godot 3.5, or use PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS + 2 for any Godot 3.x up to and including 3.5):
export(Array): ""
export(Array, int): "2:"
export(Array, Array, int): "19:2:"
export(Array, int, 0, 10): "2/1:0,10"
export(Array, Array, int, 0, 10): "19:2/1:0,10"
export(Array, int, "Red", "Green", "Blue"): "2/3:Red,Green,Blue"
export(Array, Array, int, "Red", "Green", "Blue"): "19:2/3:Red,Green,Blue"
export(Array, float): "3:"
export(Array, Array, float): "19:3:"
export(Array, float, 0, 100, 10): "3/1:0,100,10"
export(Array, Array, float, 0, 100, 10): "19:3/1:0,100,10"
export(Array, Texture): "17/17:Texture"
export(Array, Array, Texture): "19:17/17:Texture"
What kind of experimentation I did to find this out? I exported some variables and had a look at what was reported by get_property_list, and then tried different combinations in _get_property_list to see what worked and what didn't, what was necessary and what wasn't. Then I looked at Godot source code as sanity check.
By the way, the last documented hint constant PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS with value 21 in Godot 3.0, 22 in Godot 3.2 to Godot 3.4, and value 24 in Godot 3.5. But there are values beyond it, see the source code.

Related

Grails convert String to Map with comma in string values

I want convert string to Map in grails. I already have a function of string to map conversion. Heres the code,
static def StringToMap(String reportValues){
Map result=[:]
result=reportValues.replace('[','').replace(']','').replace(' ','').split(',').inject([:]){map,token ->
List tokenizeStr=token.split(':');
tokenizeStr.size()>1?tokenizeStr?.with {map[it[0]?.toString()?.trim()]=it[1]?.toString()?.trim()}:tokenizeStr?.with {map[it[0]?.toString()?.trim()]=''}
map
}
return result
}
But, I have String with comma in the values, so the above function doesn't work for me. Heres my String
[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]
my function returns ABC only. not ABC, INC. I googled about it but couldnt find any concrete help.
Generally speaking, if I have to convert a Stringified Map to a Map object I try to make use of Eval.me. Your example String though isn't quite right to do so, if you had the following it would "just work":
// Note I have added '' around the values.
​String a = "[program_type:'', subsidiary_code:'', groupName:'', termination_date:'', effective_date:'', subsidiary_name:'ABC']"
Map b = Eval.me(a)​
// returns b = [program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC]
If you have control of the String then if you can create it following this kind of pattern, it would be the easiest solution I suspect.
In case it is not possible to change the input parameter, this might be a not so clean and not so short option. It relies on the colon instead of comma values.
​String reportValues = "[program_type:, subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
reportValues = reportValues[1..-2]
def m = reportValues.split(":")
def map = [:]
def length = m.size()
m.eachWithIndex { v, i ->
if(i != 0) {
List l = m[i].split(",")
if (i == length-1) {
map.put(m[i-1].split(",")[-1], l.join(","))
} else {
map.put(m[i-1].split(",")[-1], l[0..-2].join(","))
}
}
}
map.each {key, value -> println "key: " + key + " value: " + value}
BTW: Only use eval on trusted input, AFAIK it executes everything.
You could try messing around with this bit of code:
String tempString = "[program_type:11, 'aa':'bb', subsidiary_code:, groupName:, termination_date:, effective_date:, subsidiary_name:ABC, INC]"
List StringasList = tempString.tokenize('[],')
def finalMap=[:]
StringasList?.each { e->
def f = e?.split(':')
finalMap."${f[0]}"= f.size()>1 ? f[1] : null
}
println """-- tempString: ${tempString.getClass()} StringasList: ${StringasList.getClass()}
finalMap: ${finalMap.getClass()} \n Results\n finalMap ${finalMap}
"""
Above produces:
-- tempString: class java.lang.String StringasList: class java.util.ArrayList
finalMap: class java.util.LinkedHashMap
Results
finalMap [program_type:11, 'aa':'bb', subsidiary_code:null, groupName:null, termination_date:null, effective_date:null, subsidiary_name:ABC, INC:null]
It tokenizes the String then converts ArrayList by iterating through the list and passing each one again split against : into a map. It also has to check to ensure the size is greater than 1 otherwise it will break on f[1]

How to handle a seq as a return value in Nim

I am running into a problem with Nim sequences and returning them from a function.
json_p.nim(42, 33) template/generic instantiation from here
json_p.nim(28, 22) Error: no generic parameters allowed for seq
Line 28 is where I define my key_list
proc get_json_keys(json_data: JsonNode) : seq =
var key_list: seq[string] = #[] # 28
var key: string
for record in json_data:
for key, value in record:
if (not key_list.contains(key)):
key_list.add(key)
return key_list
I just call it from a main.
proc main() : void =
var file = get_url()
var json_data = file.parseFile()
[...]
var key_list = get_json_keys(json_data)
for key in key_list:
echo key
The code works fine inside the main function.
Problems:
*seq is a generic dynamic array an you can only add the key and all search will be linear since it's like the C language array.
*All functions returning value have a default "result" named variable. You should use it to return your values.
*Using ".contains" will make nim search the entire array in order to check. The best option is use a container with fast search.
I'll assume you need:
*a function to handle json duplicate keys and return the unique list with fast key search.
Implementation:
import json,tables
proc get_json_keys(json : JsonNode):OrderedTable[string,string]=
#initialize the result object
result = initOrderedTable[string,string]()
#more info,see https://nim-lang.org/docs/json.html
for key,value in json.pairs():
#debugging...
#echo key & "-" & value.getStr()
if not result.contains(key):
result[key]=value.getStr()
var json_data = parseJson("""{"key1" :"value1","key2" :"value2" }""")
var key_list = get_json_keys(json_data)
for key in key_list.pairs() :
echo key
Output:
(Field0: "key1", Field1: "value1")
(Field0: "key2", Field1: "value2")
If search speed is not a issue, you can also do this way:
Implementation using seq:
proc get_json_keys(json : JsonNode):seq[string]=
result = newSeq[string](0)
for key,value in json.pairs():
if not result.contains(key):
result.add(key)
var json_data = parseJson("""{"key1" :"value1","key2" :"value2","key1":"value3" }""")
var key_list = get_json_keys(json_data)
echo key_list
Output:
#["key1", "key2"]
obs: edited my answer because seq is not immutable if declared with 'var'. It's only immutable if declared with 'let'.

String interpolation in Swift

A function in swift takes any numeric type in Swift (Int, Double, Float, UInt, etc).
the function converts the number to a string
the function signature is as follows :
func swiftNumbers <T : NumericType> (number : T) -> String {
//body
}
NumericType is a custom protocol that has been added to numeric types in Swift.
inside the body of the function, the number should be converted to a string:
I use the following
var stringFromNumber = "\(number)"
which is not so elegant, PLUS : if the absolute value of the number is strictly inferior to 0.0001 it gives this:
"\(0.000099)" //"9.9e-05"
or if the number is a big number :
"\(999999999999999999.9999)" //"1e+18"
is there a way to work around this string interpolation limitation? (without using Objective-C)
P.S :
NumberFormater doesn't work either
import Foundation
let number : NSNumber = 9_999_999_999_999_997
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 20
formatter.minimumIntegerDigits = 20
formatter.minimumSignificantDigits = 40
formatter.string(from: number) // "9999999999999996.000000000000000000000000"
let stringFromNumber = String(format: "%20.20f", number) // "0.00000000000000000000"
Swift String Interpolation
1) Adding different types to a string
2) Means the string is created from a mix of constants, variables, literals or expressions.
Example:
let length:Float = 3.14
var breadth = 10
var myString = "Area of a rectangle is length*breadth"
myString = "\(myString) i.e. = \(length)*\(breadth)"
Output:
3.14
10
Area of a rectangle is length*breadth
Area of a rectangle is length*breadth i.e. = 3.14*10
Use the Swift String initializer: String(format: <#String#>, arguments: <#[CVarArgType]#>)
For example:
let stringFromNumber = String(format: "%.2f", number)
String and Characters conforms to StringInterpolationProtocol protocol which provide more power to the strings.
StringInterpolationProtocol - "Represents the contents of a string literal with interpolations while it’s being built up."
String interpolation has been around since the earliest days of Swift, but in Swift 5.0 it’s getting a massive overhaul to make it faster and more powerful.
let name = "Ashwinee Dhakde"
print("Hello, I'm \(name)")
Using the new string interpolation system in Swift 5.0 we can extend String.StringInterpolation to add our own custom interpolations, like this:
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Date) {
let formatter = DateFormatter()
formatter.dateStyle = .full
let dateString = formatter.string(from: value)
appendLiteral(dateString)
}
}
Usage: print("Today's date is \(Date()).")
We can even provide user-defined names to use String-Interpolation, let's understand with an example.
extension String.StringInterpolation {
mutating func appendInterpolation(JSON JSONData: Data) {
guard
let JSONObject = try? JSONSerialization.jsonObject(with: JSONData, options: []),
let jsonData = try? JSONSerialization.data(withJSONObject: JSONObject, options: .prettyPrinted) else {
appendInterpolation("Invalid JSON data")
return
}
appendInterpolation("\n\(String(decoding: jsonData, as: UTF8.self))")
}
}
print("The JSON is \(JSON: jsonData)")
Whenever we want to provide "JSON" in the string interpolation statement, it will print the .prettyPrinted
Isn't it cool!!

Leading zeros for Int in Swift

I'd like to convert an Int in Swift to a String with leading zeros. For example consider this code:
for myInt in 1 ... 3 {
print("\(myInt)")
}
Currently the result of it is:
1
2
3
But I want it to be:
01
02
03
Is there a clean way of doing this within the Swift standard libraries?
Assuming you want a field length of 2 with leading zeros you'd do this:
import Foundation
for myInt in 1 ... 3 {
print(String(format: "%02d", myInt))
}
output:
01
02
03
This requires import Foundation so technically it is not a part of the Swift language but a capability provided by the Foundation framework. Note that both import UIKit and import Cocoa include Foundation so it isn't necessary to import it again if you've already imported Cocoa or UIKit.
The format string can specify the format of multiple items. For instance, if you are trying to format 3 hours, 15 minutes and 7 seconds into 03:15:07 you could do it like this:
let hours = 3
let minutes = 15
let seconds = 7
print(String(format: "%02d:%02d:%02d", hours, minutes, seconds))
output:
03:15:07
With Swift 5, you may choose one of the three examples shown below in order to solve your problem.
#1. Using String's init(format:_:) initializer
Foundation provides Swift String a init(format:_:) initializer. init(format:_:) has the following declaration:
init(format: String, _ arguments: CVarArg...)
Returns a String object initialized by using a given format string as a template into which the remaining argument values are substituted.
The following Playground code shows how to create a String formatted from Int with at least two integer digits by using init(format:_:):
import Foundation
let string0 = String(format: "%02d", 0) // returns "00"
let string1 = String(format: "%02d", 1) // returns "01"
let string2 = String(format: "%02d", 10) // returns "10"
let string3 = String(format: "%02d", 100) // returns "100"
#2. Using String's init(format:arguments:) initializer
Foundation provides Swift String a init(format:arguments:) initializer. init(format:arguments:) has the following declaration:
init(format: String, arguments: [CVarArg])
Returns a String object initialized by using a given format string as a template into which the remaining argument values are substituted according to the user’s default locale.
The following Playground code shows how to create a String formatted from Int with at least two integer digits by using init(format:arguments:):
import Foundation
let string0 = String(format: "%02d", arguments: [0]) // returns "00"
let string1 = String(format: "%02d", arguments: [1]) // returns "01"
let string2 = String(format: "%02d", arguments: [10]) // returns "10"
let string3 = String(format: "%02d", arguments: [100]) // returns "100"
#3. Using NumberFormatter
Foundation provides NumberFormatter. Apple states about it:
Instances of NSNumberFormatter format the textual representation of cells that contain NSNumber objects and convert textual representations of numeric values into NSNumber objects. The representation encompasses integers, floats, and doubles; floats and doubles can be formatted to a specified decimal position.
The following Playground code shows how to create a NumberFormatter that returns String? from a Int with at least two integer digits:
import Foundation
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 2
let optionalString0 = formatter.string(from: 0) // returns Optional("00")
let optionalString1 = formatter.string(from: 1) // returns Optional("01")
let optionalString2 = formatter.string(from: 10) // returns Optional("10")
let optionalString3 = formatter.string(from: 100) // returns Optional("100")
For left padding add a string extension like this:
Swift 5.0 +
extension String {
func padLeft(totalWidth: Int, with byString: String) -> String {
let toPad = totalWidth - self.count
if toPad < 1 {
return self
}
return "".padding(toLength: toPad, withPad: byString, startingAt: 0) + self
}
}
Using this method:
for myInt in 1...3 {
print("\(myInt)".padLeft(totalWidth: 2, with: "0"))
}
Swift 3.0+
Left padding String extension similar to padding(toLength:withPad:startingAt:) in Foundation
extension String {
func leftPadding(toLength: Int, withPad: String = " ") -> String {
guard toLength > self.characters.count else { return self }
let padding = String(repeating: withPad, count: toLength - self.characters.count)
return padding + self
}
}
Usage:
let s = String(123)
s.leftPadding(toLength: 8, withPad: "0") // "00000123"
Swift 5
#imanuo answers is already great, but if you are working with an application full of number, you can consider an extension like this:
extension String {
init(withInt int: Int, leadingZeros: Int = 2) {
self.init(format: "%0\(leadingZeros)d", int)
}
func leadingZeros(_ zeros: Int) -> String {
if let int = Int(self) {
return String(withInt: int, leadingZeros: zeros)
}
print("Warning: \(self) is not an Int")
return ""
}
}
In this way you can call wherever:
String(withInt: 3)
// prints 03
String(withInt: 23, leadingZeros: 4)
// prints 0023
"42".leadingZeros(2)
// prints 42
"54".leadingZeros(3)
// prints 054
Using Swift 5’s fancy new extendible interpolation:
extension DefaultStringInterpolation {
mutating func appendInterpolation(pad value: Int, toWidth width: Int, using paddingCharacter: Character = "0") {
appendInterpolation(String(format: "%\(paddingCharacter)\(width)d", value))
}
}
let pieCount = 3
print("I ate \(pad: pieCount, toWidth: 3, using: "0") pies") // => `I ate 003 pies`
print("I ate \(pad: 1205, toWidth: 3, using: "0") pies") // => `I ate 1205 pies`
in Xcode 8.3.2, iOS 10.3
Thats is good to now
Sample1:
let dayMoveRaw = 5
let dayMove = String(format: "%02d", arguments: [dayMoveRaw])
print(dayMove) // 05
Sample2:
let dayMoveRaw = 55
let dayMove = String(format: "%02d", arguments: [dayMoveRaw])
print(dayMove) // 55
The other answers are good if you are dealing only with numbers using the format string, but this is good when you may have strings that need to be padded (although admittedly a little diffent than the question asked, seems similar in spirit). Also, be careful if the string is longer than the pad.
let str = "a str"
let padAmount = max(10, str.count)
String(repeatElement("-", count: padAmount - str.count)) + str
Output "-----a str"
The below code generates a 3 digits string with 0 padding in front:
import Foundation
var randomInt = Int.random(in: 0..<1000)
var str = String(randomInt)
var paddingZero = String(repeating: "0", count: 3 - str.count)
print(str, str.count, paddingZero + str)
Output:
5 1 005
88 2 088
647 3 647
Swift 4* and above you can try this also:
func leftPadding(valueString: String, toLength: Int, withPad: String = " ") -> String {
guard toLength > valueString.count else { return valueString }
let padding = String(repeating: withPad, count: toLength - valueString.count)
return padding + valueString
}
call the function:
leftPadding(valueString: "12", toLength: 5, withPad: "0")
Output:
"00012"
Details
Xcode 9.0.1, swift 4.0
Solutions
Data
import Foundation
let array = [0,1,2,3,4,5,6,7,8]
Solution 1
extension Int {
func getString(prefix: Int) -> String {
return "\(prefix)\(self)"
}
func getString(prefix: String) -> String {
return "\(prefix)\(self)"
}
}
for item in array {
print(item.getString(prefix: 0))
}
for item in array {
print(item.getString(prefix: "0x"))
}
Solution 2
for item in array {
print(String(repeatElement("0", count: 2)) + "\(item)")
}
Solution 3
extension String {
func repeate(count: Int, string: String? = nil) -> String {
if count > 1 {
let repeatedString = string ?? self
return repeatedString + repeate(count: count-1, string: repeatedString)
}
return self
}
}
for item in array {
print("0".repeate(count: 3) + "\(item)")
}
Unlike the other answers that use a formatter, you can also just add an "0" text in front of each number inside of the loop, like this:
for myInt in 1...3 {
println("0" + "\(myInt)")
}
But formatter is often better when you have to add suppose a designated amount of 0s for each seperate number. If you only need to add one 0, though, then it's really just your pick.

Return all the indexes of a particular substring

Is there a Scala library API method (and if not, an idiomatic way) to obtain a list of all the indexes for a substring (target) within a larger string (source)? I have tried to look through the ScalaDoc, but was not able to find anything obvious. There are SO many methods doing so many useful things, I am guessing I am just not submitting the right search terms.
For example, if I have a source string of "name:Yo,name:Jim,name:name,name:bozo" and I use a target string of "name:", I would like to get back a List[Int] of List(0, 8, 17, 27).
Here's my quick hack to resolve the problem:
def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
def recursive(index: Int, accumulator: List[Int]): List[Int] = {
if (!(index < source.size)) accumulator
else {
val position = source.indexOf(target, index)
if (position == -1) accumulator
else {
recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
}
}
}
if (target.size <= source.size) {
if (!source.equals(target)) {
recursive(0, Nil).reverse
}
else List(0)
}
else Nil
}
Any guidance you can give me replacing this with a proper standard library entry point would be greatly appreciated.
UPDATE 2019/Jun/16:
Further code tightening:
def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
def recursive(indexTarget: Int = index, accumulator: List[Int] = Nil): List[Int] = {
val position = source.indexOf(target, indexTarget)
if (position == -1)
accumulator
else
recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
}
recursive().reverse
}
UPDATE 2014/Jul/22:
Inspired by Siddhartha Dutta's answer, I tighted up my code. It now looks like this:
def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
#tailrec def recursive(indexTarget: Int, accumulator: List[Int]): List[Int] = {
val position = source.indexOf(target, indexTarget)
if (position == -1) accumulator
else
recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
}
recursive(index, Nil).reverse
}
Additionally, if I have a source string of "aaaaaaaa" and I use a target string of "aa", I would like by default to get back a List[Int] of List(0, 2, 4, 6) which skips a search starting inside of a found substring. The default can be overridden by passing "true" for the withinOverlaps parameter which in the "aaaaaaaa"/"aa" case would return List(0, 1, 2, 3, 4, 5, 6).
I am always inclined to reach into the bag of regex tricks with problems like this one. I wouldn't say it is proper, but it's a hell of a lot less code. :)
val r = "\\Qname\\E".r
val ex = "name:Yo,name:Jim,name:name,name:bozo"
val is = r.findAllMatchIn(ex).map(_.start).toList
The quotes \\Q and \\E aren't necessary for this case, but if the string you're looking for has any special characters, then it will be.
A small code to get all the indexes
call the below method as getAllIndexes(source, target)
def getAllIndexes(source: String, target: String, index: Int = 0): List[Int] = {
val targetIndex = source.indexOf(target, index)
if(targetIndex != -1)
List(targetIndex) ++ getAllIndexes(source, target, targetIndex+1)
else
List()
}

Resources