Flutter/Dart: Split string by first occurrence - dart

Is there a way to split a string by some symbol but only at first occurrence?
Example: date: '2019:04:01' should be split into date and '2019:04:01'
It could also look like this date:'2019:04:01' or this date : '2019:04:01' and should still be split into date and '2019:04:01'
string.split(':');
I tried using the split() method. But it doesn't have a limit attribute or something like that.

You were never going to be able to do all of that, including trimming whitespace, with the split command. You will have to do it yourself. Here's one way:
String s = "date : '2019:04:01'";
int idx = s.indexOf(":");
List parts = [s.substring(0,idx).trim(), s.substring(idx+1).trim()];

You can split the string, skip the first item of the list created and re-join them to a string.
In your case it would be something like:
var str = "date: '2019:04:01'";
var parts = str.split(':');
var prefix = parts[0].trim(); // prefix: "date"
var date = parts.sublist(1).join(':').trim(); // date: "'2019:04:01'"
The trim methods remove any unneccessary whitespaces around the first colon.

Just use the split method on the string. It accepts a delimiter/separator/pattern to split the text by. It returns a list of values separated by the provided delimiter/separator/pattern.
Usage:
const str = 'date: 2019:04:01';
final values = string.split(': '); // Notice the whitespace after colon
Output:

Inspired by python, I've wrote this utility function to support string split with an optionally maximum number of splits. Usage:
split("a=b=c", "="); // ["a", "b", "c"]
split("a=b=c", "=", max: 1); // ["a", "b=c"]
split("",""); // [""] (edge case where separator is empty)
split("a=", "="); // ["a", ""]
split("=", "="); // ["", ""]
split("date: '2019:04:01'", ":", max: 1) // ["date", " '2019:04:01'"] (as asked in question)
Define this function in your code:
List<String> split(String string, String separator, {int max = 0}) {
var result = List<String>();
if (separator.isEmpty) {
result.add(string);
return result;
}
while (true) {
var index = string.indexOf(separator, 0);
if (index == -1 || (max > 0 && result.length >= max)) {
result.add(string);
break;
}
result.add(string.substring(0, index));
string = string.substring(index + separator.length);
}
return result;
}
Online demo: https://dartpad.dev/e9a5a8a5ff803092c76a26d6721bfaf4

I found that very simple by removing the first item and "join" the rest of the List
String date = "date:'2019:04:01'";
List<String> dateParts = date.split(":");
List<String> wantedParts = [dateParts.removeAt(0),dateParts.join(":")];

Use RegExp
string.split(RegExp(r":\s*(?=')"));
Note the use of a raw string (a string prefixed with r)
\s* matches zero or more whitespace character
(?=') matches ' without including itself

You can use extensions and use this one for separating text for the RichText/TextSpan use cases:
extension StringExtension on String {
List<String> controlledSplit(
String separator, {
int max = 1,
bool includeSeparator = false,
}) {
String string = this;
List<String> result = [];
if (separator.isEmpty) {
result.add(string);
return result;
}
while (true) {
var index = string.indexOf(separator, 0);
print(index);
if (index == -1 || (max > 0 && result.length >= max)) {
result.add(string);
break;
}
result.add(string.substring(0, index));
if (includeSeparator) {
result.add(separator);
}
string = string.substring(index + separator.length);
}
return result;
}
}
Then you can just reference this as a method for any string through that extension:
void main() {
String mainString = 'Here was john and john was here';
print(mainString.controlledSplit('john', max:1, includeSeparator:true));
}

Just convert list to string and search
productModel.tagsList.toString().contains(filterText.toLowerCase())

Related

dxl findPlainText check does not match string

I have a subroutine that I pass a string value from a skip list. That value is compared to objects in a Doors File. But the comparison does not work.
Skip split (string s, string delim)
{
Skip skp = create
int i = 0
Regexp split = regexp "^(.*?)" delim "(.*)$"
while (split s)
{
string temp_s = s[match 2]
put(skp, i++, s[match 1] "")
s = temp_s
}
put(skp, i++, s "")
return skp
}
string getInfo( string inStr)
{
for currObj in currMod do
{
if ( findPlainText( ( currOBJ.SW_VRC ""), inStr, offsetFromFind, lengthFromFind, false ) )
{
print currOBJ.SW_VRC " matches " inStr "\n";
}
}
}
Skip newLst = split(modname, ",") // this just splits a string input into parameters separated by commas
string inputInfo;
find(newLst, 0, inputInfo)
getInfo(inputInfo)
Now this is a simplified version of what I am doing. But the findPlainText does not match anything. inputInfo is getting the correct string, I checked.
The part that really kills me is if I hardcode in the parameter
i.e. inStr = "21";
It works like it's supposed to.
Now I was assuming a string is a string. Is there a difference between a string from a skip list and a string that's quoted? Is there a hidden character?
What am I missing? Any insight you could provide would be welcome.
Thanks,
DevM
your snippet works, but I had to add some variables to make it work, and I don't have a split function at hand:
Skip split (string s, string delim)
{
Skip skp = create
int i = 0
Regexp split = regexp "^(.*?)" delim "(.*)$"
while (split s)
{
string temp_s = s[match 2]
put(skp, i++, s[match 1] "")
s = temp_s
}
put(skp, i++, s "")
return skp
}
string SW_VRC="Object Text"
Module currMod = current Module
string getInfo( string inStr)
{
int offsetFromFind, lengthFromFind
string resultString = ""
Object currObj
for currObj in currMod do
{
if ( findPlainText( ( currObj.SW_VRC ""), inStr, offsetFromFind, lengthFromFind, false ) )
{
print currObj.SW_VRC " matches " inStr "\n";
resultString = resultString "\n" currObj.SW_VRC ""
}
}
return resultString
}
string modname = "a,b,cc,ccc,d,e,f"
Skip newLst = split (modname,",")
string inputInfo= "";
find(newLst, 0, inputInfo)
getInfo(inputInfo)
Perhaps you removed too much information when preparing this post?
Or you don't have an entry with the key "0" in newLst? What happens if you start your function with
string inputInfo
inputInfo = "21"
getInfo (inputInfo)
?

What is the best way to trim a trailing character in Dart?

In Dart the trim(), trimLeft() and trimRight() string methods do not take a parameter to specify unwanted non-whitespace characters.
What is the best way to trim a specific character from the ends of a string in Dart?
I am using this for now, but it feels hard to remember and not very generic:
final trailing = RegExp(r"/+$");
final trimmed = "test///".replaceAll(trailing, "");
assert(trimmed == "test");
There is no specific functionality to trim non-whitespace from the end of a string.
Your RegExp based approach is reasonable, but can be dangerous when the character you want to remove is meaningful in a RegExp.
I'd just make my own function:
String removeTrailing(String pattern, String from) {
if (pattern.isEmpty) return from;
var i = from.length;
while (from.startsWith(pattern, i - pattern.length)) i -= pattern.length;
return from.substring(0, i);
}
Then you can use it as:
final trimmed = removeTrailing("/", "test///")
assert(trimmed == "test");
The corresponding trimLeading function would be:
String trimLeading(String pattern, String from) {
if (pattern.isEmpty) return from;
var i = 0;
while (from.startsWith(pattern, i)) i += pattern.length;
return from.substring(i);
}
Since the existing answer by lrn has a lot of problems - including infinite loop scenarios - I thought I'd post my version.
String trimLeft(String from, String pattern){
if( (from??'').isEmpty || (pattern??'').isEmpty || pattern.length>from.length ) return from;
while( from.startsWith(pattern) ){
from = from.substring(pattern.length);
}
return from;
}
String trimRight(String from, String pattern){
if( (from??'').isEmpty || (pattern??'').isEmpty || pattern.length>from.length ) return from;
while( from.endsWith(pattern) ){
from = from.substring(0, from.length-pattern.length);
}
return from;
}
String trim(String from, String pattern){
return trimLeft(trimRight(from, pattern), pattern);
}
To trim all trailing/right characters by specified characters, use the method:
class StringUtil {
static String trimLastCharacters(String srcStr, String pattern) {
if (srcStr.length > 0) {
if (srcStr.endsWith(pattern)) {
final v = srcStr.substring(0, srcStr.length - 1 - pattern.length);
return trimLastCharacters(v, pattern);
}
return srcStr;
}
return srcStr;
}
}
For example, you want to remove all 0 behind the decimals
$23.67890000
then, invoke the method
StringUtil.trimLastCharacters("$23.67890000", "0")
finally, got the output:
$23.6789

How can I insert a space character in every upper case letter expect the first one at each element of string array in DXL script?

I would like to edit the elements of string array with DXL script which is used in for loop. The problem will be described in the following:
I would like to insert space in front of every upper case letter expect the first one and it would be applied for all lines in string array.
Example:
There is a string array:
AbcDefGhi
GhiDefAbc
DefGhiAbc
etc.
and finally I would like to see the result as:
Abc Def Ghi
Ghi Def Abc
Def Ghi Abc
etc.
Thanks in advance!
Derived straightly from the DXL manual..
Regexp upperChar = regexp2 "[A-Z]"
string s = "yoHelloUrban"
string sNew = ""
while (upperChar s) {
sNew = sNew s[ 0 : (start 0) - 1] " " s [match 0]
s = s[end 0 + 1:]
}
sNew = sNew s
print sNew
You might have to tweak around the fact that you do not want EVERY capital letter to be replaced with , only those that are not at the beginning of your string.
Here's a solution written as a function that you can just drop into your code. It processes an input string character by character. Always outputs the first character as-is, then inserts a space before any subsequent upper-case character.
For efficiency, if processing a large number of strings, or very large strings (or both!), the function could be modified to append to a buffer instead of a string, before finally returning a string.
string spaceOut(string sInput)
{
const int intA = 65 // DECIMAL 65 = ASCII 'A'
const int intZ = 90 // DECIMAL 90 = ASCII 'Z'
int intStrLength = length(sInput)
int iCharCounter = 0
string sReturn = ""
sReturn = sReturn sInput[0] ""
for (iCharCounter = 1; iCharCounter < intStrLength; iCharCounter++)
{
if ((intOf(sInput[iCharCounter]) >= intA)&&(intOf(sInput[iCharCounter]) <= intZ))
{
sReturn = sReturn " " sInput[iCharCounter] ""
}
else
{
sReturn = sReturn sInput[iCharCounter] ""
}
}
return(sReturn)
}
print(spaceOut("AbcDefGHi"))

How to get all strings between particular delimiters?

I have a string called source. This string contains tags, marked with number signs (#) on left and right side.
What is the most efficient way to get tag names from the source string.
Source string:
let source = "Here is tag 1: ##TAG_1##, tag 2: ##TAG_2##."
Expected result:
["TAG_1", "TAG_2"]
Not a very short solution, but here you go:
let tags = source.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: " ,."))
.filter { (str) -> Bool in
return str.hasSuffix("##") && str.hasPrefix("##")
}
.map { (str) -> String in
return str.stringByReplacingOccurrencesOfString("##", withString: "")
}
Split the string at all occurences of ##:
let components = source.components(separatedBy: "##")
// Result: ["Here is tag 1: ", "TAG_1", ", tag 2: ", "TAG_2", "."]
Check that there's an odd number of components, otherwise there's an odd amount of ##s:
guard components.count % 2 == 1 else { fatalError("Unbalanced delimiters") }
Get every second element:
components.enumerated().filter{ $0.offset % 2 == 1 }.map{ $0.element }
In a single function:
import Foundation
func getTags(source: String, delimiter: String = "##") -> [String] {
let components = source.components(separatedBy: delimiter)
guard components.count % 2 == 1 else { fatalError("Unbalanced delimiters") }
return components.enumerated().filter{ $0.offset % 2 == 1 }.map{ $0.element }
}
getTags(source: "Here is tag 1: ##TAG_1##, tag 2: ##TAG_2##.") // ["TAG_1", "TAG_2"]
You can read this post and adapt the answer for your needs: Swift: Split a String into an array
If not you can also create your own method, remember a string is an array of characters, so you can use a loop to iterate through and check for a '#'
let strLength = source.characters.count;
var strEmpty = "";
for( var i=0; i < strLength; i++ )
{
if( source[ i ] == '#' )
{
var j=(i+2);
for( j; source[ (i+j) ] != '#'; j++ )
strEmpty += source[ (i+j) ]; // concatenate the characters to another variable using the += operator
i = j+2;
// do what you need to with the tag
}
}
I am more of a C++ programmer than a Swift programmer, so this is how I would approach it if I didn't want to use standard methods. There may be a better way of doing it, but I don't have any Swift knowledge.
Keep in mind if this does not compile then you may have to adapt the code slightly as I do not have a development environment I can test this in before posting.

How to Tokenize String with Commas and Line Delimiter

I'm making a simple String Tokenizer in Swift like I would in Java...but it's really not working out for me.
The end of each line in my data source delimited with "^" and the data is separated by comma's.
For example: "string 1,string 2,string 3,^,string 1,string 2,string 3,^"
This is what I would do in Java...(I only want the first two strings in each line of data)
String delimeter = "^";
StringTokenizer tokenizedString = new StringTokenizer(responseString,delimeter);
String [] stringArray = new String [tokenizedString.countTokens()];
StringTokenizer tokenizedAgain;
String str1;
String str2;
String token;
for(int i =0; i< stringArray.length; i ++)
{
token = tokenizedString.nextToken();
tokenizedAgain = new StringTokenizer(token, ",");
tokenizedAgain.nextToken();
str1 = tokenizedAgain.nextToken();
str2 = tokenizedAgain.nextToken();
}
If someone could point me in the right direction that would really helpful.
I've looked at this: Swift: Split a String into an array
and this: http://www.swift-studies.com/blog/2014/6/23/a-swift-tokenizer
but I can't really find other resources on String Tokenizing in Swift. Thanks!
This extends Syed's componentsSeperatedByString answer but with Swift's map to create the requested Nx2 matrix.
let tokenizedString = "string 1, string 2, string 3, ^, string a, string b, string c, ^"
let lines = tokenizedString.componentsSeparatedByString("^, ")
let tokens = lines.map {
(var line) -> [String] in
let token = line.componentsSeparatedByString(", ")
return [token[0], token[1]]
}
println(tokens)
var delimiter = "^"
var tokenDelimiter = ","
var newstr = "string 1, string 2, string 3, ^, string 1, string 2, string 3,^"
var line = newstr.componentsSeparatedByString(delimiter) // splits into lines
let nl = line.count
var tokens = [[String]]() // declares a 2d string array
for i in 0 ..< nl {
let x = line[i].componentsSeparatedByString(tokenDelimiter) // splits into tokens
tokens.append(x)
}
println(tokens[0][0])

Resources