Kotlin - How to split the string into digits and string? - arrays

I want to split a long string (containing digits and characters) into different substrings in Kotlin?
eg:- Buy these 2 products and get 100 off
output needed -> "Buy these ","2 ","products and get ","100 ","off"

Use a regular expression that will match either a group of digits, or a group of non-digit characters.
I used \d+|\D+.
\d+ will match groups of digits, like 2 and 100
\D+ will match any sequence of characters that doesn't contain digits
The | indicates that the pattern should match either \d+ or \D+
Use findAll to find each part of the string that matches the pattern.
val regex = Regex("""\d+|\D+""")
val input = "Buy these 2 products and get 100 off"
val result = regex.findAll(input).map { it.groupValues.first() }.toList()
The result I get is:
["Buy these ", "2", " products and get ", "100", " off"]
The spacing isn't quite the same as what you're looking for, so you could trim the results, or adjust the regex pattern.

this code also works well in this example:
val str = "Buy these 2 products and get 100 off"
val result = str.split(regex = Regex("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"))

Related

How to loop assigning characters in a string to variable?

I need to take a string and assign each character to a new string variable for a Text To Speech engine to read out each character separately, mainly to control the speed at which it's read out by adding pauses in between each character.
The string contains a number which can vary in length from 6 digits to 16 digits, and I've put the below code together for 6 digits but would like something neater to handle any different character count.
I've done a fair bit of research but can't seem to find a solution, plus I'm new to Groovy / programming.
OrigNum= "12 34 56"
Num = OrigNum.replace(' ','')
sNum = Num.split("(?!^)")
sDigit1 = sNum[0]
sDigit2 = sNum[1]
sDigit3 = sNum[2]
sDigit4 = sNum[3]
sDigit5 = sNum[4]
sDigit6 = sNum[5]
Edit: The reason for needing a new variable for each character is the app that I'm using doesn't let the TTS engine run any code. I have to specifically declare a variable beforehand for it to be read out
Sample TTS input: "The number is [var:sDigit1] [pause] [var:sDigit2] [pause]..."
I've tried using [var:sNum[0]] [var:sNum[1]] to read from the map instead but it is not recognised.
Read this about dynamically creating variable names.
You could use a map in your stuation, which is cleaner and more groovy:
Map digits = [:]
OrigNum.replaceAll("\\s","").eachWithIndex { digit, index ->
digits[index] = digit
}
println digits[0] //first element == 1
println digits[-1] //last element == 6
println digits.size() // 6
Not 100% sure what you need, but to convert your input String to output you could use:
String origNum = "12 34 56"
String out = 'The number is ' + origNum.replaceAll( /\s/, '' ).collect{ "[var:$it]" }.join( ' [pause] ' )
gives:
The number is [var:1] [pause] [var:2] [pause] [var:3] [pause] [var:4] [pause] [var:5] [pause] [var:6]

Regex string with 2+ different numbers and some optional characters in Snowflake syntax

I would like to check if a specific column in one of my tables meets the following conditions:
String must contain at least three characters
String must contain at least two different numbers [e.g. 123 would work but 111 would not]
Characters which are allowed in the string:
Numbers (0-9)
Uppercase letters
Lowercase letters
Underscores (_)]
Dashes (-)
I have some experience with Regex but am having issues with Snowflake's syntax. Whenever I try using the '?' regex character (to mark something as optional) I receive an error. Can someone help me understand a workaround and provide a solution?
What I have so far:
SELECT string,
LENGTH(string) AS length
FROM tbl
WHERE REGEXP_LIKE(string,'^[0-9]+{3,}[-+]?[A-Z]?[a-z]?$')
ORDER BY length;
Thanks!
Your regex looks a little confusing and invalid, and it doesn't look like it quite meets your needs either. I read this expression as a string that:
Must start with one or more digits, at least 3 or more times
The confusing part to me is the '+' is a quantifier, which is not quantifiable with {3,} but somehow doesn't produce an error for me
Optionally followed by either a dash or plus sign
Followed by an uppercase character zero or one times (giving back as needed)
Followed by and ending with a lowercase character zero or one times (giving back as needed)
Questions
You say that your string must contain 3 characters and at least 2 different numbers, numbers are characters but I'm not sure if you mean 3 letters...
Are you considering the numbers to be characters?
Does the order of the characters matter?
Can you provide an example of the error you are receiving?
Notes
Checking for a second digit that is not the same as the first involves the concept of a lookahead with a backreference. Snowflake does not support backreferences.
One thing about pattern matching with regular expressions is that order makes a difference. If order is not of importance to you, then you'll have multiple patterns to match against.
Example
Below is how you can test each part of your requirements individually. I've included a few regexp_substr functions to show how extraction can work to check if something exists again.
Uncomment the WHERE clause to see the dataset filtered. The filters are written as expressions so you can remove any/all of the regexp_* columns.
select randstr(36,random(123)) as r_string
,length(r_string) AS length
,regexp_like(r_string,'^[0-9]+{3,}[-+]?[A-Z]?[a-z]?$') as reg
,regexp_like(r_string,'.*[A-Za-z]{3,}.*') as has_3_consecutive_letters
,regexp_like(r_string,'.*\\d+.*\\d+.*') as has_2_digits
,regexp_substr(r_string,'(\\d)',1,1) as first_digit
,regexp_substr(r_string,'(\\d)',1,2) as second_digit
,first_digit <> second_digit as digits_1st_not_equal_2nd
,not(regexp_instr(r_string,regexp_substr(r_string,'(\\d)',1,1),1,2)) as first_digit_does_not_appear_again
,has_3_consecutive_letters and has_2_digits and first_digit_does_not_appear_again as test
from table(generator(rowcount => 10))
//where regexp_like(r_string,'.*[A-Za-z]{3,}.*') // has_3_consecutive_letters
// and regexp_like(r_string,'.*\\d+.*\\d+.*') // has_2_digits
// and not(regexp_instr(r_string,regexp_substr(r_string,'(\\d)',1,1),1,2)) // first_digit_does_not_appear_again
;
Assuming the digits need to be contiguous, you can use a javascript UDF to find the number in a string with with the largest number of distinct digits:
create or replace function f(S text)
returns float
language javascript
returns null on null input
as
$$
const m = S.match(/\d+/g)
if (!m) return 0
const lengths = m.map(m=> [...new Set (m.split(''))].length)
const max_length = lengths.reduce((a,b) => Math.max(a,b))
return max_length
$$
;
Combined with WHERE-clause, this does what you want, I believe:
select column1, f(column1) max_length
from t
where max_length>1 and length(column1)>2 and column1 rlike '[\\w\\d-]+';
Yielding:
COLUMN1 | MAX_LENGTH
------------------------+-----------
abc123def567ghi1111_123 | 3
123 | 3
111222 | 2
Assuming this input:
create or replace table t as
select * from values ('abc123def567ghi1111_123'), ('xyz111asdf'), ('123'), ('111222'), ('abc 111111111 abc'), ('12'), ('asdf'), ('123 456'), (null);
The function is even simpler if the digits don't have to be contiguous (i.e. count the distinct digits in a string). Then core logic changes to:
const m = S.match(/\d/g)
if (!m) return 0
const length = [...new Set (m)].length
return length
Hope that's helpful!

Improve code with checking element in array is digit

I want to check that each element in String is digit. Firstly, I split the String to an Array by a regexp [, ]+ expression and then I try to check each element by forall and isDigit.
object Test extends App {
val st = "1, 434, 634, 8"
st.split("[ ,]+") match {
case arr if !arr.forall(_.forall(_.isDigit)) => println("not an array")
case arr if arr.isEmpty => println("bad delimiter")
case _ => println("success")
}
}
How can I improve this code and !arr.forall(_.forall(_.isDigit))?
Use matches that requires the string to fully match the pattern:
st.matches("""\d+(?:\s*,\s*\d+)*""")
See the Scala demo and the regex demo.
Details
In a triple quoted string literal, there is no need to double escape backslashes that are part of regex escapes
Anchors - ^ and $ - are implicit when the pattern is used with .matches
The regex means 1+ digits followed with 0 or more repetitions of a comma enclosed with 0 or more whitespaces and then 1+ digits.
I think it can be simplified while also making it a bit more robust.
val st = "1,434 , 634 , 8" //a little messier but still valid
st.split(",").forall(" *\\d+ *".r.matches) //res0: Boolean = true
I'm assuming strings like "1,,,434 , 634 2 , " should fail.
The regex can be put in a variable so that it is compiled only once.
val digits = " *\\d+ *".r
st.split(",").forall(digits.matches)

Valid regex expressions won't work with AngularJS

I'd like to check if user input is correct for phone numbers in two formats:
01 1234567 (two numbers, a space, seven numbers)
+12 123 123 123 123 (plus sign, two numbers, a space, three numbers, a space, three numbers, a space, three numbers
no character at all (no input)
I wrote a regex for this [0-9]{2} [0-9]{3} [0-9]{3} [0-9]{3}|[0-9]{2} [0-9]{7}|. It works when checked with online regex checkers, but it won't work (user can write whatever they want) when used with AngularJS: ng-pattern="[0-9]{2} [0-9]{3} [0-9]{3} [0-9]{3}|[0-9]{2} [0-9]{7}|".
You need to define a regex that will match the whole string that matches your patterns as optional patterns:
ng-pattern="/^(?:\+[0-9]{2} [0-9]{3} [0-9]{3} [0-9]{3}|[0-9]{2} [0-9]{7})?$/"
^^ ^^
Or, a bit shorter:
ng-pattern="/^(?:\+[0-9]{2}(?: [0-9]{3}){3}|[0-9]{2} [0-9]{7})?$/"
If you define the pattern in a JS file as a variable use
var mypattern = /^(?:\+[0-9]{2}(?: [0-9]{3}){3}|[0-9]{2} [0-9]{7})?$/;
Note that when using regex delimiters the anchors are required for the regex to match entire input.
See the regex demo.
Details
^ - start of string
(?: - start of an optional non-capturing group:
\+ - a + char
[0-9]{2} [0-9]{3} [0-9]{3} [0-9]{3} (equal to [0-9]{2}(?: [0-9]{3}){3}) - 2 digits and then 3 occurrences of a space, 3 digits
| - or
[0-9]{2} [0-9]{7} - 2 digits, space, 7 digits
)? - end of the optional group
$ - end of string.

StringRegExp pattern not working properly

I have this string:
{"name": "Fancy HaXXor123Name","profession": 1,"race": 2,"map_id": 1052,"world_id": 268435461,"team_color_id": 0,"commander": false,"fov": 0.768}
I want to get an array back which includes the following information (from left to right from the string):
Fancy HaXXor123Name
1
2
1052
268435461
0
false
0.768
I tried to mess with RegExBuddy and got a promissing pattern which looks like this
(\d{1,}).(\d{1,})|(\d{1,})|(?i)"(.*?)"
This is what I got back
name
Fancy HaXXor123Name
profession
1
race
2
map_id
10
2
world_id
2684354
1
team_color_id
0
commander
fov
0
768
So there are large spaces between the informations, torn numbers and the false is missing. I can't fix this problem and I'm completely new to StringRegExp.
I'm using AutoIT which uses the PCRE RegExp-Engine (this is what think).
You may use a regex like the following:
"\s*:\s*(?:"\K[^"]*|\K[^][\s,{}]+)
See the regex demo
Details:
"\s*:\s* - a literal ", 0+ whitespaces, :, 0+ whitespaces
(?:"\K[^"]*|\K[^][\s,{}]+) - A non-capturing group matching 2 alternatives:
"\K[^"]* - a ", then \K zeros the text matched so far, and then matches 0+ chars other than " with [^"]*
\K[^][\s,{}]+ - \K drops the text matched so far, and [^][\s,{}]+ matches 1+ chars other than ], [, whitespace, ,, { and }.

Resources