I want to find an one-line function to implement substringAfter in scala, which is the same as commons-lang's StringUtils.substringAfter().
def substringAfter(str:String, key:String) = ...
Some tests:
substringAfter("1234512345", "23") // ==> 4512345
substringAfter("1234512345", "a") // ==> ""
substringAfter("1234512345", "") // ==> 1234512345
substringAfter("", "23") // ==> ""
No need to consider null str here.
For now, I have such a solution:
def substringAfter(s:String,k:String) = {
s.indexOf(k) match {
case -1 => ""
case i => s.substring(i+k.length)
}
}
How to get an one-line one?
def substringAfter(s:String,k:String) = { s.indexOf(k) match { case -1 => ""; case i => s.substring(i+k.length) } }
Less efficient, but slightly shorter:
def substringAfter(s: String, k: String) =
s.tails.find(_.startsWith(k)).map(_.drop(k.length)).getOrElse("")
or if regexes are your thing:
def substringAfter(s: String, k: String) =
(".*?(?:\Q"+k+"\E)(.*)").r.unapplySeq(s).flatten.mkString
Your version rewritten to use a Map with default function:
def substringAfter(s: String, k: String) =
Map(-1 -> "").withDefault(i => s.substring(i + k.length))(s.indexOf(k))
My friend gave me a solution:
def substringAfter(str:String, key:String) =
str.stripPrefix(str.take(str.indexOf(key))+key)
Related
When I know name of one of fields in expected object, I can use
if [check_if_this_is_an_object][known_field_name] { # object } else { # not an object }
But I would like a way to do a general check, without known field name.
You will need to use a ruby filter is you do not know the name of any of the fields within the object. If you just need to check one field you could use
ruby {
code => '
field = event.get("foo")
if field.respond_to? "each"
answer = true
elsif field.is_a? LogStash::Timestamp
answer = true
else
answer = false
end
event.set("[#metadata][fooObject]", answer)
'
}
If you need to check multiple fields then use a script file. The script would be
def register(params)
#fieldName = params["field"]
end
def filter(event)
field = event.get(#fieldName)
if field.respond_to? "each" # Array or Hash
answer = true
elsif field.is_a? LogStash::Timestamp
answer = true
else # boolean, integer, float or string
answer = false
end
event.set("[#metadata][#{#fieldName}Object]", answer)
[event]
end
and then call it using
ruby {
path => "/home/user/isObject.rb"
script_params => { "field" => "foo" }
}
ruby {
path => "/home/user/isObject.rb"
script_params => { "field" => "bar" }
}
and then you can write a conditional using [#metadata][fooObject] or [#metadata][barObject]
I'm trying to transform a list of strings:
def description = """user:some.mail#gmail.com;groups:Team-1, Team-2
user:some.othermail#gmail.com;groups:Team-2, Team-3
user:another.mail#gmail.com;groups:Team-1, Team-3
some other text"""
description = description.split('\\r\\n|\\n|\\r').findAll { it.startsWith('user') }
into a map so it looked like this:
[some.mail#gmail.com: "Team-2, Team-3", some.othermail#gmail.com: "Team-2, Team-3", another.mail#gmail.com: "Team-1, Team-3"]
so I could later iterate getting an e-mail address and corresponding teams.
Sadly with the below code I was only able to partly achieve it and only for one item of the list. I'm stuck with getting it into a loop and get the full result.
def userData = [:]
userData = description[0].split(';').inject([:]) { map, token ->
token.split(':').with {
map[it[0].trim()] = it[1].trim()
}
map
}
Can you give me a hint as for how I could get a map with all the items from the list?
You can use collectEntries method on a list:
def description = """user:some.mail#gmail.com;groups:Team-1, Team-2
user:some.othermail#gmail.com;groups:Team-2, Team-3
user:another.mail#gmail.com;groups:Team-1, Team-3
some other text"""
description = description.split('\\r\\n|\\n|\\r').findAll { it.startsWith('user') }
def map = description.collectEntries {
// split "user:some.mail#gmail.com;groups:Team-1, Team-2"
def split = it.split(';')
// remove "user:" prefix
def email = split[0].split(':')[1]
// remove "groups:" prefix
def groups = split[1].split(':')[1]
// create a map entry
[(email), groups]
}
Then running map.forEach {k, v -> println "key: '${k}', value: '${v}'"} prints following: (standard map to string may be a little bit chaotic in this case)
key: 'some.mail#gmail.com', value: 'Team-1, Team-2'
key: 'some.othermail#gmail.com', value: 'Team-2, Team-3'
key: 'another.mail#gmail.com', value: 'Team-1, Team-3'
Working on an internal website where the URL contains the source reference from other systems. This is a business requirement and cannot be changed.
i.e. "http://localhost:9000/source.address.com/7808/project/repo"
"http://localhost:9000/build.address.com/17808/project/repo"
I need to remove these strings from the "project/repo" string/variables using a trait so this can be used natively from multiple services. I also want to be able to add more sources to this list (which already exists) and not modify the method.
"def normalizePath" is the method accessed by services, 2 non-ideal but reasonable attempts so far. Getting stuck on a on using foldLeft which I woudl like some help with or an simpler way of doing the described. Code Samples below.
1st attempt using an if-else (not ideal as need to add more if/else statements down the line and less readable than pattern match)
trait NormalizePath {
def normalizePath(path: String): String = {
if (path.startsWith("build.address.com/17808")) {
path.substring("build.address.com/17808".length, path.length)
} else {
path
}
}
}
and 2nd attempt (not ideal as likely more patterns will get added and it generates more bytecode than if/else)
trait NormalizePath {
val pattern = "build.address.com/17808/"
val pattern2 = "source.address.com/7808/"
def normalizePath(path: String) = path match {
case s if s.startsWith(pattern) => s.substring(pattern.length, s.length)
case s if s.startsWith(pattern2) => s.substring(pattern2.length, s.length)
case _ => path
}
}
Last attempt is to use an address list(already exists elsewhere but defined here as MWE) to remove occurrences from the path string and it doesn't work:
trait NormalizePath {
val replacements = (
"build.address.com/17808",
"source.address.com/7808/")
private def remove(path: String, string: String) = {
path-string
}
def normalizePath(path: String): String = {
replacements.foldLeft(path)(remove)
}
}
Appreciate any help on this!
If you are just stripping out those strings:
val replacements = Seq(
"build.address.com/17808",
"source.address.com/7808/")
replacements.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
case(path, toReplace) => path.replaceAll(toReplace, "")
}
// http://localhost:9000/project/repo
If you are replacing those string by something else:
val replacementsMap = Seq(
"build.address.com/17808" -> "one",
"source.address.com/7808/" -> "two/")
replacementsMap.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
case(path, (toReplace, replacement)) => path.replaceAll(toReplace, replacement)
}
// http://localhost:9000/two/project/repo
The replacements collection can come from elsewhere in the code and will not need to be redeployed.
// method replacing by empty string
def normalizePath(path: String) = {
replacements.foldLeft(path){
case(startingPoint, toReplace) => startingPoint.replaceAll(toReplace, "")
}
}
normalizePath("foobar/build.address.com/17808/project/repo")
// foobar/project/repo
normalizePath("whateverPath")
// whateverPath
normalizePath("build.address.com/17808build.address.com/17808/project/repo")
// /project/repo
A very simple replacement could be made as follows:
val replacements = Seq(
"build.address.com/17808",
"source.address.com/7808/")
def normalizePath(path: String): String = {
replacements.find(path.startsWith(_)) // find the first occurrence
.map(prefix => path.substring(prefix.length)) // remove the prefix
.getOrElse(path) // if not found, return the original string
}
Since the expected replacements are very similar, have you tried to generalize them and use regex matching?
There are a million and one ways to extract /project/repo from a String in Scala. Here are a few I came up with:
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
path.stripPrefix(list.find(x => path.contains(x)).getOrElse(""))
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
list.map(x => if (path.contains(x)) {
path.takeRight(path.length - x.length)
}).filter(y => y != ()).head
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: Any = /project/repo
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
list.foldLeft(path)((a, b) => a.replace(b, ""))
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo
Depends how complicated you want your code to look (or how silly you want to be), really. Note that the second example has return type Any, which might not be ideal for your scenario. Also, these examples aren't meant to be able to just take the String out of the middle of your path... they can be fairly easily modified if you want to do that though. Let me know if you want me to add some examples just stripping things like build.address.com/17808 out of a String - I'd be happy to do so.
I am looking for a way to replace a word inside a string in swift. Can anyone help?
this is what I have so far, I can find the specific word, but i do not know how to replace it...
var str = "helo, playgound"
var findWords = ["helo","playgound"]
var replaceWords = ["hello","playground"]
extension String {
var wordList:[String] {
return "".join(componentsSeparatedByCharactersInSet(NSCharacterSet.punctuationCharacterSet())).componentsSeparatedByString(" ")
}
}
func stringToArray() -> Array<String> {
var arr = str.wordList
return arr
}
func correction(var _arr:Array<String>) -> String{
for var i = 0; i < _arr.count; i++ {
if str.lowercaseString.rangeOfString(findWords[i]) != nil {
println("exists")
}
}
return str
}
It depends what your definition of a "word" is. If you're looking for an intelligent built-in notion of a "word", the easiest solution is probably to use NSRegularExpression, which knows where "word" boundaries are:
var s = NSMutableString(string:"hello world, go to hell")
let r = NSRegularExpression(
pattern: "\\bhell\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: "heaven")
After that, s is "hello world, go to heaven", which is the right answer; we replaced the "hell" that is a word, but not the "hell" in "hello". Notice that we are also matching case-insensitively, which seems to be one of your desiderata.
That example shows how do just one pair ("hell" and "heaven") but it is easy to abstract it into a method so that you can do it again and again for further pairs:
var str = "helo, playgound"
var findWords = ["helo", "playgound"]
var replaceWords = ["hello", "playground"]
func correct(str:String, orig:String, repl:String) -> String {
var s = NSMutableString(string:str)
let r = NSRegularExpression(
pattern: "\\b\(orig)\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: repl)
return s
}
for pair in Zip2(findWords,replaceWords) {
str = correct(str, pair.0, pair.1)
}
str // hello, playground
The easiest is probably this:
let statement = "Swift is hard."
let swiftRange = statement.startIndex..<advance(statement.startIndex, 5)
let newStatement = statement.stringByReplacingCharactersInRange(swiftRange, withString: "Objective-C")
// now newStatement = "Objective-C is hard."
Following a longer commenting tour: The above is under the assumption of the OP "I can find the specific word, but i do not know how to replace it...", so it's not about finding a "word" which to define is another discussion. It's just about replacing an already found word.
Another word on stringByReplacingCharactersInRange: #matt states that this is Cocoa cross-over. In that case Apple is telling a plain lie:
I fostered the web but there's no Apple source telling anything. Only the Foundation method for NSString. Their Swift book is silent too (in many respects). Well, I don't trust Apple anyway any longer since Yosemite-fail.
I have a tsv file in the form of "key \t value", and I need to read into a map. Currently i do it like this:
referenceFile.eachLine { line ->
def (name, reference) = line.split(/\t/)
referencesMap[name.toLowerCase()] = reference
}
Is there a shorter/nicer way to do it?
It's already quite short. Two answers I can think of:
First one avoids the creation of a temporary map object:
referenceFile.inject([:]) { map, line ->
def (name, reference) = line.split(/\t/)
map[name.toLowerCase()] = reference
map
}
Second one is more functional:
referenceFile.collect { it.split(/\t/) }.inject([:]) { map, val -> map[val[0].toLowerCase()] = val[1]; map }
The only other way I can think of doing it would be with an Iterator like you'd find in Commons IO:
#Grab( 'commons-io:commons-io:2.4' )
import org.apache.commons.io.FileUtils
referencesMap = FileUtils.lineIterator( referenceFile, 'UTF-8' )
.collectEntries { line ->
line.tokenize( '\t' ).with { k, v ->
[ (k.toLowerCase()): v ]
}
}
Or with a CSV parser:
#Grab('com.xlson.groovycsv:groovycsv:1.0')
import static com.xlson.groovycsv.CsvParser.parseCsv
referencesMap = referenceFile.withReader { r ->
parseCsv( [ separator:'\t', readFirstLine:true ], r ).collectEntries {
[ (it[ 0 ].toLowerCase()): it[ 1 ] ]
}
}
But neither of them are shorter, and not necessarily nicer either...
Though I prefer option 2 as it can handle cases such as:
"key\twith\ttabs"\tvalue
As it deals with quoted strings
This is the comment tim_yates added to melix's answer, and I think it's the shortest/clearest answer:
referenceFile.collect { it.tokenize( '\t' ) }.collectEntries { k, v -> [ k.toLowerCase(), v ] }