Making a variable the key in a Map - dart

I have two classes
class Phone extends Observable
{
#observable String type = '';
#observable String provider = '';
#observable String num = '';
Map<String, Map<String, String> map = {};
Phone() {}
Phone.build({ this.type,
this.provider,
this.num });
}
I have attempted to use the field values as the key in map like so
Phone phone = new Phone();
phone.map[phone.type] = {'type':'cell', 'provider':'Verizon', 'num':'1234567'};
but it does not work. How can I make the fields value the key for the map?

just remove the quotes
phone.map[phone.type] = {type:'cell', provider:'Verizon', num:'1234567'};
Because you are using strings in your example this may not apply but be aware that if you use instances of custom types as Map key...
The keys of a `HashMap` must have consistent [Object.operator==]
and [Object.hashCode] implementations. This means that the `==` operator
must define a stable equivalence relation on the keys (reflexive,
anti-symmetric, transitive, and consistent over time), and that `hashCode`
must be the same for objects that are considered equal by `==`.

Related

In dart, is Map treated as a value type?

I have heard that all data types in dart are reference types, but some fairly primitive ones such as int behave as value types. Does Map behave as a value type?
I had wrapped a class around my Map and it worked:
final manageVgnItmFormsProvider = Provider.autoDispose<VgnItmForms>((provider) {
return VgnItmForms();
});
class VgnItmForms {
late Map<String, VgnItmFormVm> forms;
VgnItmForms();
}
And I changed it to:
final manageVgnItmFormsProvider = Provider.autoDispose<Map>((provider) {
var forms = {};
return forms;
});
And then the forms Map kept going back to being an empty Map after I had set it to a populated Map elsewhere. I think it is getting lost because Map is treated as a value type and before my class was treated as a reference type.

Dart equatable with non final variables

I have class with final variable (hash) and some other non-final variables.
Hash is unique value. And objects are stored in Set. Set uses '==' operand to check equality of objects. I want to override "==" and "hashCode" in my class and work with Set array.
To avoid using boilerplate code I want to use Equatable extension. Like this
class User extends Equatable {
final String hash;
String balance;
bool state;
....
#override
List<Object> get props => [hash];
} .... Set<User> users
Is it correct way to use Equatable in my case, 'cause it is uses with immutable classes.
Thanks!
Overriding hashCode to depend on non-final fields is usually not recommended because it can make Sets and Maps and other data structures that depend on hashCode internally inconsistent. Suppose you have such an object and insert it into a Set. Later, you mutate that object by assigning a new value to that field, but the Set would still have a reference to that object with the old hash code. For example, consider:
class Foo {
String s;
Foo(this.s);
#override
bool operator ==(Object other) {
return other is Foo && s == other.s;
}
#override
int get hashCode => s.hashCode;
#override
String toString() => s;
}
void main() {
var foo = Foo('foo');
var someSet = <Foo>{foo};
foo.s = 'bar';
print(someSet.contains(foo)); // Prints: false
someSet.add(foo);
print(someSet.length); // Prints: 2
print(someSet); // Prints: {bar, bar}
}
and now someSet would have two references to the exact same object, which violates its goal of storing unique objects.
A Map would have similar problems.
Since hashCode is tied to operator ==, this consequently also means that you usually shouldn't override operator == to depend on non-final fields.
You can get away with it if you can guarantee that you never mutate your objects while they're being referenced by a Set/Map/etc. or if you can guarantee that whenever you want to add your object to a Set/Map/etc. that you create a copy of your object and add that copy instead.
I'd say your use is correct.
You use Equatable only with the final field, so the equality and hash code should be stable over time, and the hash field is unique, so it can serve as identifier for the object.
If your hash field ends up not unique, you'll have two distinguishable objects that are equal, so ... don't do that.
Not sure how much you gain from using Equatable, though. If you wrote the equality and hash code yourself, it would just be:
bool operator==(Object other) => other is Foo && hash == other.hash;
int get hashCode => hash.hashCode;

How to convert List<dynamic> to List<T> without getting warning from linter in Dart?

I wrote this code to convert dynamic list to Word list but linter says:
Omit type annotations for local variables. on 2nd line.
However if I omit type annotations, I get an error A value of type 'List<dynamic>' can't be returned from method 'convert' because it has a return type of 'List<Word>'.
It there any smarter way to convert?
static List<Word> convert(List<dynamic> words) {
final List<Word> wordsList = [];
words.forEach((v) {
final map = Map<String, dynamic>.from(v as Map<dynamic, dynamic>);
wordsList.add(Word.fromMap(map));
});
return wordsList;
}
Word.fromMap is:
Word.fromMap(Map<String, dynamic> map)
: text = map['text'] as String,
count = map['count'] as int;
To avoid the warning, and put the type on the right-hand side as the lint wants, just write:
final wordsList = <Word>[];
I assume words is JSON data, so the maps are already Map<String, dynamic>. Then you can also do everything in one line:
static List<Word> convert(List<dynamic> words) =>
[for (var v in words) Word.fromMap(v)];
Use the cast() method like this:
class Word {
final String text;
final int count;
static List<Word> convert(List<dynamic> words) {
final List<Word> wordsList = [];
words.cast<Map<dynamic, dynamic>>().forEach((v) { // <-- look here
final map = Map<String, dynamic>.from(v);
wordsList.add(Word.fromMap(map));
});
return wordsList;
}
Word.fromMap(Map<String, dynamic> map)
: text = map['text'] as String,
count = map['count'] as int;
}
It will ensure the casting are done on each element. Make sure the type are correct since it will else result in a type-cast error.

How does the Dart URI class QueryParameters handle Map values?

According to the documentation, it needs to follows the Form Post rules at: https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4. When looking at that information it did not give me much to work with in terms of complex objects or maps.
Right now, If I have a list for example: Each item in the list needs to be stringified.
var params = {"list": [1,2,3]};
// needs to be stringed.
params["list"] = params["list"].map((item)=>item.toString()).toList();
Simple. Also all base items need to be a string as well
var params = {"number": 1, "boolean": true};
params = params.forEach((k,v)=> params[k].toString());
But how do we handle maps?
var params = {"map": {"a":1,"b":"foo","c":false,"d":[]}};
// ??
It seems that after testing in my app and in dart pad, you need to make sure everything is strings, so i am trying to come up with a way to effectively cover lists, maps, and maybe more complex objects for encoding.
var params = {};
params["list"] = [1,2,3];
params["number"] = 1;
params["boolean"] = true;
params["map"] = {"a":1,"b":"foo","c":false,"d":[]};
params.forEach((String key, dynamic value){
if(value is List){
params[key] = value.map((v)=>v.toString()).toList();
}else if(value is Map){
// ????
}else{
params[key] = value.toString();
}
//maybe have an additional one for custom classes, but if they are being passed around they should already have their own JSON Parsing implementations.
}
Ideally, the result of this would be passed into:
Uri myUri = new Uri(queryParameters: params);
and right now, while i solved the list issue, it doesn't like receiving maps. Part of me just wanted to stringify the map as a whole, but i wasn't not sure if there was a better way. I know that when someone accidentally stringified the array, it was not giving me: ?id=1&id=2 but instead ?id=%5B1%2C2%5D which was not correct.
I don't think there is any special support for maps. Query parameters itself is a map from string to string or string to list-of-strings.
Everything else need to be brought into this format first before you can pass it as query parameter.
A simple approach would be to JSON encode the map and pass the resulting string as a single query parameter.

Neo4j BatchInserter using setNodeProperties to set an array with values

I am using the BatchInserter instance to get and set properties for nodes.
My data have multiple values in some properties.
property value
======== =========
synonym animal
synonym mammalian
I want to put this values, in the same property in the same node.
I have used the following code snippet to read and set values:
String[] values = {"animal", "mammalian"};
for (int i = 0; i < values.length(); i++) {
Map<String, Object> nodeProps = db.getNodeProperties(0); // Node 0 properties
nodeProps.put("synonym", values[i]);
db.setNodeProperties(0, nodeProps);
}
In the first iteration property synonym gets value animal. In the second iteration, the property is overridden by the value mammalian.
My question is: How can I get the previous value(s), add the new one and set the property to the node so I can get synonym=['animal', 'mammalian'] in graph?
If you want to have multiple values on a node/relationship property you need to use arrays. So you have on property synonyms with a String[] as value:
To amend an existing property:
String[] values = {"animal", "mammalian"};
Map<String, Object> nodeProps = db.getNodeProperties(0);
String[] existingValues = nodeProps.get("synonym");
// using org.apache.commons.lang.ArrayUtils from Apache Commons Lang:
String[] amendedValues = ArrayUtils.addAll(existingValues, values);
nodeProps.put("synonym", amendedValues);
db.setNodeProperties(0, nodeProps);

Resources