Test if string contains anything from an array of strings (kotlin)
Asked Answered
S

4

31

I'm new to Kotlin (I have a Java background) and I can't seem to figure out how to check whether a string contains a match from a list of keywords.

What I want to do is check if a string contains a match from an array of keywords (case-insensitive please). If so, print out the keyword(s) that was matched and the string that contained the keyword. (I will be looping over a bunch of strings in a file).

Here's an MVE for starters:

val keywords = arrayOf("foo", "bar", "spam")

fun search(content: String) {
    var match = <return an array of the keywords that content contained>
    if(match.size > 0) {
          println("Found match(es): " + match + "\n" + content)
    }
}   

fun main(args: Array<String>) {
    var str = "I found food in the barn"
    search(str) //should print out that foo and bar were a match
}

As a start (this ignores the 'match' variable and getting-a-list-of-keywords-matched), I tried using the following if statement according with what I found at this question,

if(Arrays.stream(keywords).parallel().anyMatch(content::contains))

but it put a squiggly line under "content" and gave me this error

None of the following functions can be called with the arguments supplied: public operator fun CharSequence.contains(char: Char, ignoreCase: Boolean = ...): Boolean defined in kotlin.text public operator fun CharSequence.contains(other: CharSequence, ignoreCase: Boolean = ...): Boolean defined in kotlin.text @InlineOnly public inline operator fun CharSequence.contains(regex: Regex): Boolean defined in kotlin.text

Spoof answered 10/6, 2018 at 23:14 Comment(0)
V
41

You can use the filter function to leave only those keywords contained in content:

val match = keywords.filter { it in content }

Here match is a List<String>. If you want to get an array in the result, you can add .toTypedArray() call.

in operator in the expression it in content is the same as content.contains(it).

If you want to have case insensitive match, you need to specify ignoreCase parameter when calling contains:

val match = keywords.filter { content.contains(it, ignoreCase = true) }
Viviennevivify answered 10/6, 2018 at 23:31 Comment(2)
Looks great, but another question though: how could I change search to a boolean function that only returns whether or not a keyword was found in content? Is there an easy one-liner solution to that?Spoof
Replace filter with any.Conductance
N
4

Another obvious choice is using a regex doing case-insensitive matching:

arrayOf("foo", "bar", "spam").joinToString(prefix = "(?i)", separator = "|").toRegex())

Glues together a pattern with a prefixed inline (?i) incase-sensitive modifier, and alternations between the keywords: (?i)foo|bar|spam

Sample Code:

private val keywords = arrayOf("foo", "bar", "spam")
private val pattern = keywords.joinToString(prefix = "(?i)", separator = "|")
private val rx = pattern.toRegex()

fun findKeyword(content: String): ArrayList<String> { 
    var result = ArrayList<String>()
    rx.findAll(content).forEach { result.add(it.value) }
    return result
}

fun main(args: Array<String>) { 
    println(findKeyword("Some spam and a lot of bar"));
}

The regex approach could be handy if you are after some more complex matching, e.g. non-/overlapping matches adding word boundaries \b, etc.

Newfashioned answered 11/6, 2018 at 1:34 Comment(2)
How would I modify it to match whole keywords only? (Eg If my keyword is 'foo', I only want to match the word 'foo' in the sentence, and not 'food')Spoof
@Spoof keywords.joinToString(prefix = "(?i)\\b", separator = "\\b|\\b", postfix = "\\b")Newfashioned
U
3

Here is my approach without Streams:

fun String.containsAnyOfIgnoreCase(keywords: List<String>): Boolean {
    for (keyword in keywords) {
        if (this.contains(keyword, true)) return true
    }
    return false
}

Usage:

"test string".containsAnyOfIgnoreCase(listOf("abc","test"))
Unyoke answered 23/6, 2020 at 12:49 Comment(1)
Why would you rewrite the wheel?Carnation
L
3

I think Any is the efficient way.

fun findMatch(s: String, strings: List<String>): Boolean {
    return strings.any { s.contains(it) }
}
 
fun main() {
    val today = "Wednesday"
    val weekend = listOf("Sat", "Sun")
    println(if (findMatch(today, weekend)) "Yes" else "No") // No
}

reference: click here

Lurk answered 8/8, 2022 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.