How to split a string by multiple delimiters in Dart - dart

If I have a string like this:
final myFancyCodez = "AA-BB.CC_DD.EE";
how can I split it on more than one delimiter?
expect(fancy.multiSplit( ["-", "."] ), ["AA", "BB", "CC_DD", "EE"] );
expect(fancy.multiSplit( [] ), ["AA-BB.CC_DD.EE"] );
expect(fancy.multiSplit( ['_'] ), ["AA-BB.CC", "DD.EE"] );

I would personally use something like this instead where we combine the separators into a single regular expression and use the split method:
import 'package:test/test.dart';
void main() {
const fancy = "AA-BB.CC_DD.EE";
test('Some tests', () {
expect(fancy.multiSplit(["-", "."]), ["AA", "BB", "CC_DD", "EE"]);
expect(fancy.multiSplit([]), ["AA-BB.CC_DD.EE"]);
expect(fancy.multiSplit(['_']), ["AA-BB.CC", "DD.EE"]);
});
}
extension UtilExtensions on String {
List<String> multiSplit(Iterable<String> delimeters) => delimeters.isEmpty
? [this]
: this.split(RegExp(delimeters.map(RegExp.escape).join('|')));
}

Here is one way of doing it:
extension UtilExtensions on String {
List<String> multiSplit(Iterable<String> delimeters) {
if (delimeters?.isEmpty ?? true) return [this];
if (delimeters.length == 1) return this.split(delimeters.first);
final next = delimeters.skip(1);
return this
.split(delimeters.first)
.expand((i) => i.multiSplit(next))
.toList();
}
}

Related

How to do a switchmap or any better way to replace values in RxDart

I have a collection with chatroom information. Something like this:
{
chatroomid: 59,
members: [2,3]
}
Now what I want to do is, get the collection stream, in the course of doing that be able to replace the members string ids with a corresponding firestore document based on member id.
End result should look something like this:
{
chatroomid: 59,
members: [{
id: 2,
username: Johndoe1
},
{
id: 3,
username: Jennydoe1
}]
}
Is this possible with Dart RxDart?
Trying something like this fails:
getChatroomStream(chatroomid)
.switchMap((i) {
return Stream.value(i.members.map((e) => i.members.add(Document(path: 'Global.userRef/$e').streamData())));
})
.listen((event) {print(event);});
[VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: Concurrent
modification during iteration: Instance of
'MappedListIterable<dynamic, void>'.
#0 ListIterator.moveNext (dart:_internal/iterable.dart:337:7)
class MemberInfo {
final int id;
final String? username;
final bool isLoading;
}
class Room {
final int chatroomid;
final List<int> members;
}
class RoomWithMemberInfos {
final int chatroomid;
final List<MemberInfo> infos;
factory RoomWithMemberInfos.initial(Room room) {
return RoomWithMemberInfos(
room.chatroomid,
room.members
.map((id) => MemberInfo(id, null, true))
.toList(growable: false)
);
}
RoomWithMemberInfos withInfo(MemberInfo info) {
return RoomWithMemberInfos(
chatroomid,
infos
.map((e) => e.id == info.id ? MemberInfo(e.id, info.name, false) : e)
.toList(growable: false)
);
}
}
Stream<MemberInfo> getMemberInfo(int id) { ... }
getChatroomStream()
.switchMap((Room room) {
final initial = RoomWithMemberInfos.initial(room);
return Stream
.fromIterable(room.members)
.flatMap(getMemberInfo)
.scan<RoomWithMemberInfos>(
(acc, info) => acc!.withInfo(info),
initial,
)
.startWith(initial);
});

how to filter null values from map in Dart

Following the map, having both key-value pair as dynamic, Write a logic to filter all the null values from Map without us?
Is there any other approach than traversing through the whole map and filtering out the values (Traversing whole map and getting Entry Object and discarding those pairs) ?
I need to remove all values that are null and return map
Map<String, dynamic> toMap() {
return {
'firstName': this.firstName,
'lastName': this.lastName
};
Use removeWhere on Map to remove entries you want to filter out:
void main() {
final map = {'text': null, 'body': 5, null: 'crap', 'number': 'ten'};
map.removeWhere((key, value) => key == null || value == null);
print(map); // {body: 5, number: ten}
}
And if you want to do it as part of your toMap() method you can do something like this with the cascade operator:
void main() {
print(A(null, 'Jensen').toMap()); // {lastName: Jensen}
}
class A {
final String? firstName;
final String? lastName;
A(this.firstName, this.lastName);
Map<dynamic, dynamic> toMap() {
return <dynamic, dynamic>{
'firstName': this.firstName,
'lastName': this.lastName
}..removeWhere(
(dynamic key, dynamic value) => key == null || value == null);
}
}
You can now use a map literal with conditional entries:
Map<String, dynamic> toMap() => {
if (firstName != null) 'firstName': firstName,
if (lastName != null) 'lastName': lastName,
};
I did this to make it easy remove nulls from map and list using removeWhere:
https://dartpad.dartlang.org/52902870f633da8959a39353e96fac25
Sample:
final data =
{
"name": "Carolina Ratliff",
"company": null,
"phone": "+1 (919) 488-2302",
"tags": [
"commodo",
null,
"dolore",
],
"friends": [
{
"id": 0,
"name": null,
"favorite_fruits": [
'apple', null, null, 'pear'
]
},
{
"id": 1,
"name": "Pearl Calhoun"
},
],
};
void main() {
// From map
print('Remove nulls from map:\n' + data.removeNulls().toString());
// From list
print('\nRemove nulls from list:\n' + [data].removeNulls().toString());
}
The limitation of removeWhere is that it does not check for nested values. Use this recursive solution if you want to remove all keys in the hierarchy.
dynamic removeNull(dynamic params) {
if (params is Map) {
var _map = {};
params.forEach((key, value) {
var _value = removeNull(value);
if (_value != null) {
_map[key] = _value;
}
});
// comment this condition if you want empty dictionary
if (_map.isNotEmpty)
return _map;
} else if (params is List) {
var _list = [];
for (var val in params) {
var _value = removeNull(val);
if (_value != null) {
_list.add(_value);
}
}
// comment this condition if you want empty list
if (_list.isNotEmpty)
return _list;
} else if (params != null) {
return params;
}
return null;
}
Example:
void main() {
Map<String, dynamic> myMap = {
"a": 1,
"b": 2,
"c": [
3,
4,
null,
{"d": 7, "e": null, "f": 5}
],
"g": {"h": null, "i": null},
"j": 6,
"h": []
};
print(removeNull(myMap));
}
Output:
{a: 1, b: 2, c: [3, 4, {d: 7, f: 5}], j: 6}
Note:
If you want an empty map and list when their child has null values, comment out an empty check for map and list in the code.
I suggest you to use removeWhere function
Map<String, dynamic> map = {
'1': 'one',
'2': null,
'3': 'three'
};
map.removeWhere((key, value) => key == null || value == null);
print(map);
Maybe, just like I did, someone might come looking, for How to remove null fields from a model that will be sent to the API using retrofit and dio in flutter
Here is what I used to remove null values from the Model object
#Body(nullToAbsent: true)
For example
#POST("/shops")
Future<Shop> createShop(#Body(nullToAbsent: true) Shop shop);
The nullToAbsent param does the trick
This is the method I am using with Flutter 3:
Map<String, Object?> removeAllNulls(Map<String, Object?> map) {
final data = {...map};
data.removeWhere((key, value) => value == null);
for (final entry in data.entries.toList()) {
final value = entry.value;
if (value is Map<String, Object?>) {
data[entry.key] = removeAllNulls(value);
} else if (value is List<Object?>) {
final list = List.from(value);
for (var i = 0; i < list.length; i++) {
final obj = list[i];
if (obj is Map<String, Object?>) {
list[i] = removeAllNulls(obj);
}
}
data[entry.key] = list;
}
}
return data;
}
Usage:
var jsonData = jsonDecode(jsonEncode(example.toJson()));
jsonData = removeAllNulls(jsonData);
Remove null from JSON Using Dart Extension Method
extension JsonExtension on Map<String, dynamic> {
Map<String, dynamic> get removeNull {
removeWhere((String key, dynamic value) => value == null);
return this;
}
}
final data = {
"hello":null,
"world":"test"
};
void main() {
print(data.removeNull);
}
Output
{world: test}

type 'JSArray<dynamic>' is not a subtype of type 'List<Ad>'

I tried two method to parse the rawData into dart objects. One using a for loop ads and it works but why _ads is not working when I use map ?
void main() {
dynamic rawData = [
{"title": "a", "id": 1}
];
List<Ad> ads = [];
for (var raw in rawData) {
Ad ad = Ad.fromJson(raw);
ads.add(ad);
}
print(ads);
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
print(_ads);
}
class Ad {
Ad({
this.id,
this.title,
});
int id;
String title;
factory Ad.fromJson(Map<String, dynamic> json) => _$AdFromJson(json);
Map<String, dynamic> toJson() => _$AdToJson(this);
}
Ad _$AdFromJson(Map json) {
return Ad(
id: json['id'] as int,
title: json['title'] as String,
);
}
Map<String, dynamic> _$AdToJson(Ad instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('id', instance.id);
writeNotNull('title', instance.title);
return val;
}
The result of calling nearly any method on a dynamic defined variable are going to be dynamic since the Dart compiler are going through a difficult time guessing the type you want. So when you want the result to be saved into a variable with a specific type like List<Ad> _ads you really need to tell the compiler at each step what generic type you want and expect.
With that said, you can get you code to work by changing:
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
Into:
List<Ad> _ads = rawData.map<Ad>((e) => Ad.fromJson(e)).toList();
And if you also want to make the analyzer happy:
List<Ad> _ads = rawData.map<Ad>((Map<String, dynamic> e) => Ad.fromJson(e)).toList() as List<Ad>;

In dart how can I get a map of fields from a template and a string?

var template = '/test/:key1/tester/:key2';
var filledIn = '/test/12/tester/3';
How can I get an map like this given inputs above?
[
key1: 12,
key2: 3
]
Here is a better solution that gives the exact format you asked for.
void main() {
final template = Template('/test/:key1/tester/:key2');
print(template.parse('/test/12/tester/3')); // {key1: 12, key2: 3}
}
class Template {
Template(this.source);
final String source;
List<String> _parts;
Iterable<int> _params;
List<String> get parts => _parts ??= source.split('/');
Iterable<int> get params => _params ??= parts
.where((part) => part.startsWith(':'))
.map((param) => parts.indexOf(param));
Map<String, String> parse(String string) {
final p = string.split('/');
return Map.fromEntries(
params.map((index) => MapEntry(parts[index].substring(1), p[index])));
}
}
The following works but it may not be the most elegant solution.
Map.from() is used because List.asMap() uses an Unmodifiable list.
final template = '/test/:key1/tester/:key2';
final filledIn = '/test/12/tester/3';
final params = Map.from(template.split('/').asMap())
..removeWhere((i, part) => !part.startsWith(':'));
final values = Map.from(filledIn.split('/').asMap())
..removeWhere((i, part) => !params.containsKey(i));
print(params.values); // [:key1, :key2]
print(values.values); // [12, 3]

Obscure Dart syntax

While working through the the Dart Route library example for client side coding I came across this snippet.
var router = new Router()
..addHandler(urls.one, showOne)
..addHandler(urls.two, showTwo)
..addHandler(urls.home, (_) => null)
..listen();
My question is how does (_) => null work? It seems to specify a function that returns a null value but what does (_) mean?
(_) means it is a function with one parameter but you don't care about that parameter, so it's just named _. You could also write (ignoreMe) => null. The important thing here is, that there needs to be a function that accepts one parameter. What you do with it, is your thing.
(_) => null means : a function that take one parameter named _ and returning null. It could be seen as a shortcut for (iDontCareVariable) => null.
A similar function with no parameter would be () => null.
A similar function with more parameters would be (_, __, ___) => null.
Note that _ is not a special syntax defined at langauge level. It is just a variable name that can be used inside the function body. As example : (_) => _.
I will try explain this by the example.
void main() {
var helloFromTokyo = (name) => 'こんにちわ $name';
var greet = new Greet();
greet.addGreet('London', helloFromLondon)
..addGreet('Tokyo', helloFromTokyo)
..addGreet('Berlin', helloFromBerlin)
..addGreet('Mars', (_) => null)
..addGreet('Me', (name) => 'Privet, chuvak! You name is $name?')
..addGreet('Moon', null);
greet.greet('Vasya Pupkin');
}
String helloFromLondon(String name) {
return 'Hello, $name';
}
String helloFromBerlin(String name) {
return 'Guten tag, $name';
}
class Greet {
Map<String, Function> greets = new Map<String, Function>();
Greet addGreet(String whence, String sayHello(String name)) {
greets[whence] = sayHello;
return this;
}
void greet(String name) {
for(var whence in greets.keys) {
var action = greets[whence];
if(action == null) {
print('From $whence: no reaction');
} else {
var result = action(name);
if(result == null) {
print('From $whence: silent');
} else {
print('From $whence: $result');
}
}
}
}
}
Output:
From London: Hello, Vasya Pupkin
From Tokyo: こんにちわ Vasya Pupkin
From Berlin: Guten tag, Vasya Pupkin
From Mars: silent
From Me: Privet, chuvak! You name is Vasya Pupkin?
From Moon: no reaction

Resources