I have a list of paths like the following:
[ "animal/cat/persian", "animal/cat/british_shorthait",
"animal/dog/pug", "animal/dog/pitbull", "vehicle/car/mercedes",
"vehicle/car/bmw"]
and I need to convert it into a nested structure (Map)
{
'animal': {
'cat': {
'Persian': {},
'British_Shorthair': {}
}
'dog': {
'Pug': {},
'Pitbull': {}
}
}
'vehicle': {
'car': {
'Mercedes': {},
'BMW': {}
}
}
The paths are of varying length and can be infinitely deep.
Any ideas on how to do this in a performant manner?
This is akin to a reverse of this question
Initialize an empty Map for the results.
For each path in your list, split it into a list of keys. Use the Map from step 1 as the initial Map for each path.
For each key, retrieve the nested Map for that key in the current Map, creating it if necessary. Make the retrieved Map the current one.
import 'dart:convert';
void main() {
var mapPaths = [
"animal/cat/persian",
"animal/cat/british_shorthair",
"animal/dog/pug",
"animal/dog/pitbull",
"vehicle/car/mercedes",
"vehicle/car/bmw",
];
var mapRoot = <String, dynamic>{};
for (var path in mapPaths) {
var currentMap = mapRoot;
for (var key in path.split('/')) {
currentMap = currentMap[key] ??= <String, dynamic>{};
}
}
// Pretty-print `mapRoot`.
print(JsonEncoder.withIndent(' ').convert(mapRoot));
}
Related
I want to create a map using the path
generateNestedMap("foo.bar.baz", "someValue")
and the output should be
Output: {'foo'{'bar':{'baz': 'someValue'}}}
Run this in dartpad. No recursion required. Just build it from inside-out:
void main() {
print(generateNestedMap("foo.bar.baz", "someValue"));
}
Map generateNestedMap(String path, String payload) {
var steps = path.split('.');
Object result = payload;
for (var step in steps.reversed) {
result = {step : result};
}
return result as Map;
}
EDIT: Or, as I suggested in one of the comments, a little fancier, but cool:
void main() {
print(generateNestedMap("foo.bar.baz", "someValue"));
}
Map generateNestedMap(String path, String payload) {
var steps = path.split('.');
var result = steps.reversed.fold(payload, (old, next) => {next: old});
return result as Map;
}
I want to add the contents of a file to a list, but somehow the list seems to not get filled. Maybe we need some asynchronous code here.
Please help!
class App {
Map<String, List<Model>> models = {
'users': List<User>(),
'groups': List<Group>(),
'events': List<Event>()
};
App() {
loadUsers();
print(models['users']); // prints empty list: []
}
void loadUsers () {
Directory usersDirectory = Directory.fromUri(Uri(path: './app/data/users'));
usersDirectory.list(recursive: false).listen((FileSystemEntity userFile) async {
Map<String, dynamic> userFileContent = jsonDecode(await File(userFile.uri.path).readAsString());
models['users'].add(User.fromJson(userFileContent));
});
}
}
Thank you.
Try to get directories without listen:
usersDirectory.listSync(recursive: false).forEach((userFile) {
Map<String, dynamic> userFileContent = jsonDecode(await File(userFile.uri.path).readAsStringSync());
models['users'].add(User.fromJson(userFileContent));
);
I am using flutter webview plugin.
getCookies() will return a Future<Map<String, String>>.
the containsKey function is false even "id" exist in the Map.
I have no problem if the Map does not come from Future.
How can I access the value from Map?
flutterWebviewPlugin.getCookies().then((Map<String, String> _) {
if (_.containsKey("id")) {
print(${_["id"]});
}
});
print map by
flutterWebviewPlugin.getCookies().then((Map<String, String> _) {
print(_);
}
output of map values
{"locale: en, tcurrency: 6, id: 10702776, pbe: "}
I fix the cookie by this now
_flutterWebviewPlugin.getCookies().then((Map<String, String> cookies) {
Map<String, String> trimCookies = {};
for (String key in cookies.keys) {
trimCookies[key.replaceAll("\"", "").trim()] = cookies[key].replaceAll("\"", "");
}
}
Lets say you are trying to access deeply nested children in a map and you are not able to expect their parents to be there. Example:
Map awesomeMap = {
"this":{
"is":{
"sometimes":"not here"
}
}
}
Map notAwesomeMap = {
"this":{
"haha":{
"we":"switched"
}
}
}
When I go to access notAwesomeMap['this']['is']['sometimes'] it will return an error because ['this']['is'] is null, and you cannot look for the value ['sometimes'] of null.
So that's fine, but I was hoping to be able to use conditional member access operators...
notAwesomeMap['this']?.['is']?.['sometimes']
but that doesn't work...
Short of wrapping everything in a try block, is there a good way to handle these situations?
Edit: I tried playing around with this and I didn't find anything really illuminating, but maybe this gives someone an idea
void main() {
Map nestedMap = {
'this':{
'is':{
'sometimes':'here'
}
}
};
final mapResult = nestedMap['this'];
print(mapResult); //returns {is: {sometimes: here}}
final nullResult = nestedMap['this']['is an'];
print(nullResult); // returns null
final nullifiedResult = nullify(nestedMap['this']['is an']['error']);
print(nullifiedResult); // returns error, but is this possible another way?
final errorResult = nestedMap['this']['is an']['error'];
print(errorResult); // returns error
}
nullify(object){
try {
final result = object;
return result;
}
catch (e) {
return null;
}
}
One way would be
final result = (((nestedMap ?? const {})['this'] ?? const {})['is an'] ?? const {})['error'];
See also Null-aware operator with Maps
You could write a simple function to help do what you want:
R lookup<R, K>(Map<K, dynamic> map, Iterable<K> keys, [R defaultTo]);
Example usage:
final result = lookup(inputMap, ['this', 'is', 'something']);
Example implementation:
https://dartpad.dartlang.org/1a937b2d8cdde68e6d6f14d216e4c291
void main() {
var nestedMap = {
'this':{
'is':{
'sometimes':'here'
}
}
};
print(lookup(nestedMap, ['this']));
print(lookup(nestedMap, ['this', 'is']));
print(lookup(nestedMap, ['this', 'is', 'sometimes']));
print(lookup(nestedMap, ['this', 'is', 'error']));
// Bail out on null:
print(lookup(nestedMap, ['error'], 'Default Value'));
}
R lookup<R, K>(Map<K, dynamic> map, Iterable<K> keys, [R defaultTo]) {
dynamic current = map;
for (final key in keys) {
if (current is Map<K, dynamic>) {
current = current[key];
} else {
return defaultTo;
}
}
return current as R;
}
I like #matanlurey's approach, but have made two changes:
Drop the defaultTo since you can still use ?? which is more readable.
Swallow type-cast errors.
R lookup <R, K>(Map<K, dynamic> map, Iterable<K> keys) {
dynamic current = map;
for (final key in keys) {
if (current is Map<K, dynamic>) {
current = current[key];
}
}
try{
return current as R;
} catch(e){
// do nothing
}
}
Usage is similar
String someValue = lookup(nestedMap, ['some', 'value']) ?? 'Default Value';
I have defined a static var as Map for all instances of my element. If it contains a specific key, it should use the value. If the key is not contains the instance should get the data with a request and save it in the static map, so other instances could use it.
static var data = new Map();
func() {
if (Elem.data.containsKey(['key']) {
list = Elem.data['key'];
}
else {
Helper.getData().then((requestedData) {
list = requestedData;
Elem.data.addAll({ 'key' : requestedData });
}
}
The Problem is that all my instances go into the else, since the key is not contained in the Map at the moment the other instances are at the if. So i need them to wait, until the Data is in the Map.
static var data = new Map();
static Completer _dataCompleter;
Future<bool> func() {
if(_dataCompleter == null) {
_dataCompleter = new Completer();
Helper.getData().then((requestedData) {
list = requestedData;
Elem.data.addAll({ 'key' : requestedData });
_dataCompleter.complete(true);
})
}
if(_dataCompleter.isCompleted) {
return new Future.value(true);
}
return _dataCompleter.future;
}
and call it like
func().then((success) => /* continue here when `key` in `data` has a value.
In response to Günter Zöchbauer. I generally avoid using Completers directly:
static var data = new Map();
static Future _pendingFuture;
Future func() {
if (_pendingFuture == null) {
_pendingFuture = Helper.getData().then((requestedData) {
list = requestedData;
Elem.data.addAll({ 'key' : requestedData });
});
}
return _pendingFuture;
}