Shorter way to write this conditional statement? - dart

Is there a better alternative then coding this:
String reps = status != null && status.sets != null && status.sets[index].reps != null ? status.sets[index].reps.toString() : '-';
I could also do this:
String reps;
try {
reps = hasImprovedReps ? currentReps : status.sets[index].reps.toString();
} catch (e) {
reps = '-';
}
But this way it's not one line and it's not a condition so I could use it in a Text()constructor.

You can use ?. and ?? operators:
String reps = status?.sets?.elementAt(index)?.reps?.toString() ?? '-'

Related

Flutter/Dart: Split string by first occurrence

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())

Compare List with Value in Dart / Flutter

I am trying to compare an input value to a list of items in Dart language, however, it does not seem to work.
Method:
String validateStoreNumber(String value) {
List<String> storeList = ['55', '56', '88'];
// String patttern = r'(^[a-zA-Z ]*$)';
RegExp regExp = new RegExp(r'^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$');
if (value.length == 0) {
return "car number is required";
} else if (!regExp.hasMatch(value)) {
return "Accepts only numbers";
} else if (value.length != 2){
return "car number must have 2 digits";
} else if (value != storeList.contains(value)){
return "Not our model";
}
return null;
}
This line...
else if (value != storeList.contains(value))
Is the problem. The left side "value" is a string, and the right side "contains" is a boolean. You are comparing a string to a boolean.
I think you want this...
else if (!storeList.contains(value))

String formatting with characters is crashing

var modifiedString: String {
var outString = String()
for thisChar in self.characters {
if (thisChar == " "){
outString.appendingFormat("%%02X",thisChar ) // This line is asking me unwrap as CVarArg
outString.appendingFormat("%%02X",thisChar as! CVarArg) // This line crashes
}
}
}
}
I am trying to use outString.appendingFormat("%%02X",thisChar )
But this line is forcing me to unwrap as CVarArg but it is crashing when I am doing it. what is wrong in this
I assume this is placed in a String extension.
The error occurs because Character does not conform to CVarArg. Instead of converting the character directly to CVarArg, you need to convert it to a type that conforms to CVarArg. Here, the best choice would be String:
outString.appendingFormat("%%02X", "\(thisChar)")
You are also missing a return statement.
However, even if you did this and your code compiles, the property you wrote still does not make much sense. I don't think it works as you intended it to, but without more information I can't really be sure.
Use this code-
var modifiedString: String {
var outString = String()
for thisChar in self.characters {
if (thisChar == " "){
// outString = outString.appendingFormat("%%02X",thisChar.description )
outString = outString.appendingFormat("%%02X", "\(thisChar)")
}
else
{
outString = outString.appending("\(thisChar)")
}
}
return outString
}
Hope this helps!
It is not clear enough what you want to do, but I guess you want to do something like this:
extension String {
var modifiedString: String {
var outString = ""
for thisChar in self.characters {
if thisChar == " " {
let charCode = String(thisChar).unicodeScalars.first!.value
outString += "%" + String(format: "%02X", charCode)
//### Or if you prefer... (Do not miss, you need 3 '%'s in the format string.)
//outString = outString.appendingFormat("%%%02X", charCode)
} else {
outString.append(thisChar)
}
}
return outString
}
}
print("what you really want to do?".modifiedString)
//->what%20you%20really%20want%20to%20do?
With looping with for thisChar in self.characters, the type of thisChar becomes Character, it's not easy to get a character code from Character in Swift.
There may be some better ways but let charCode = String(thisChar).unicodeScalars.first!.value works as expected, when thisChar contains only one UnicodeScalar.
And you should better know Swift has a method named addingPercentEncoding:
let result = "what you really want to do?".addingPercentEncoding(withAllowedCharacters: CharacterSet.whitespaces.inverted)!
print(result)
//->what%20you%20really%20want%20to%20do?

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.

Component separated by string crashing app swift

I have an app that converts textField input into an array of Int's by using componentSeparatedByString(",") but when i enter more than one comma in the textfield the app crashes, been trying to find a solution online but no luck, how can i fix this? I can keep it from crashing by checking for characters.first == "," ||characters.last == ",", but not consecutive commas.
enterValueLabel.text = ""
let circuits = circuitNumbersTextField.text!.componentsSeparatedByString(",")
let circuitNumbers = circuits.map { Int($0)!}
CircuitColors(circuitNumber: circuitNumbers, phaseColors: circuitPhaseColors )
if /*circuitNumbersTextField.text!.characters.first != "," || */circuitNumbersTextField.text!.characters.last != "," || (circuitNumbersTextField.text!.characters.first != "," && circuitNumbersTextField.text!.characters.last != ",")
Here's what I would do to make your code work. What is important here is the general idea, not the specific example I'm using (although it should work for you).
First, let's safely unwrap the text label:
if let text = circuitNumbersTextField.text {
}
Now that we avoid using circuitNumbersTextField.text! we know that an error wouldn't come from there.
Then we cut the sentence in components:
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
}
We use flatMap to safely unwrap the Optionals returned by Int():
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
let circuitNumbers = circuits.flatMap { Int($0) }
// circuitNumbers will only contain the successfully unwrapped values
}
Your code snippet:
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
let circuitNumbers = circuits.flatMap { Int($0) }
if (circuits.first != "," && circuits.last != ",") || circuits.first != "," || circuits.last != "," {
// condition is met
} else {
// condition is not met
}
}
You can now safely use circuitNumbers in this code block without crashing.

Resources