Cannot modify some values in a deep-copied nested Map - dart

I'm new to Dart.
I have a constant Map that defines some kind of data schema, that way:
const Map<String, dynamic> FILE_STRUCT = {
"dt": {
"a": [],
"b": [],
"c": [],
},
"os": null,
"pm": null,
"p": "1",
};
Then, later, I want to use that structure to create a Map and add elements to it. So I deep-copy this Map to a new Map var, so I can add elements in it, that way:
Map data = {...FILE_STRUCT};
When I do data["os"] = "newValue"; I don't have any issue. But when I do:
List<List> aValues = [[1,2,3], [4,5,6]];
data["dt"]["a"] = aValues;
But when I do that, I get this error:
UnsupportedError (Unsupported operation: Cannot modify unmodifiable map)
I thought the var data was modifiable. So what is happening here, and what would be the proper way to modify the nested Map?

Related

Create unique set of maps in Dart

I need to create a unique set of maps but it seems that adding maps to a set doesn't filter and adds duplicates.
Set mySet = Set();
List myList = [
{"name": "Jane"},
{"name": "Jane"},
{"name": "Mary"}
]
for(var item in myList){
mySet.add(item);
}
Doing this causes the set to contain all three maps. Is there any way to only have one Jane?
Other than doing what #Patrick suggested, you also have to major ways:
If your map is constant just declare it as a const so they will reference always the same object, hence the Set will work as you expect to:
List myList = const [
{"name": "Jane"},
{"name": "Jane"},
{"name": "Mary"}
]
Check if a map with the same name already exists before adding the new one:
for(var item in list){
// If a map with the same name exists don't add the item.
if (set.any((e) => e['name'] == item['name'])) {
continue;
}
set.add(item);
}
Just a quick note, you can initialize a Set using its literal constructor (there is a linter rule about this):
var set = <Map<String, String>>{}
This is because the Maps are different: {"name": "Jane"} != {"name": "Jane"}.
You have to create your own class and override == (and hashCode).
See "Implementing map keys":
https://dart.dev/guides/libraries/library-tour

Elm : How to Encode Nested objects

Model
type alias Model {
name : String
, poi_coordinates : Coordinates
}
type alias Coordinates =
{
coord_type : String
, coordinates : List Float
}
poiFormEncoder : Model -> Encode.Value
poiFormEncoder model =
Encode.object
[
( "name", Encode.string model.name )
, ( "type", Encode.string model.poi_coordinates.coord_type)
, ( "poi_coordinates", Encode.array Encode.float (Array.fromList model.poi_coordinates.coordinates) )
]
Can i ask how to encode for this data type? I have no idea , and the encoder i did gives no coordinates fill. Any help is really appreciate. The Json file format is at below
[
{
"name": "Mcd",
"coordinates": {
"type": "Point",
"coordinates": [
101.856603,
2.924
]
}
},
.
.
.
]
You can nest calls to Json.Encode.object. Each time you want a new object in the output, you need another one, e.g:
poiFormEncoder : Model -> Encode.Value
poiFormEncoder model =
Encode.object
[ ( "name", Encode.string model.name )
, ( "coordinates"
, Encode.object
[ ( "type", Encode.string model.poi_coordinates.coord_type )
, ( "coordinates", Encode.list Encode.float model.poi_coordinates.coordinates )
]
)
]
This should make sense: it is a list of (key, value) pairs, and the value should be another object.
On a side note, it will depend on your use case, but your Coordinates type looks like a prime candidate for a custom Elm type, e.g:
type Coordinates
= Point { x : Float, y : Float }
| Polar { r : Float, t : Float }
| ...
If you find you are doing a lot of checking the string type value and then dealing with the coordinates accordingly, something like this might be a much nicer structure to use internally. Of course, the best representation will depend on how you are using the type.

Swift Filter same location coordinates contained inside an array of objects

I am receiving from Server, a list of Stores information in an array of objects. Below is a sample -
"stores": [
{
"name": “Store 1”,
"number": "5381",
"country": "BELGIE",
"latLng": [
50.730614,
4.231847
]
},
{
"name": "Store 2”,
"number": "5220",
"country": "BELGIE",
"latLng": [
50.730614,
4.231847
]
},
{
"name": "Store 3”,
"number": "3982”,
"country": "BELGIE",
"latLng": [
50.7315706,
4.2303477
]
},
{
"name": "Store 4”,
"number": "4179",
"country": "BELGIE",
"latLng": [
50.7262577,
4.245589
]
}]
What am I trying?:
I need to filter out the stores in the array that has same latLng values.
Why?
I need to identify these 'same latLng' values and add an offset of some value like 0.001 to the latitude value so that when I show these stores on a map, the stores on same location appear side by side.
I found this (answer by Rob B) as reference for this approach.
What I need?
1. How can I filter with values inside of an object in the array?
I tried something like this inside a for loop -
print("\(allStoresInfo.filter({ $0.latLng![0] == $0.latLng![0] }).count)")
This value always returns 4. I know I am missing some basic sense here but need know what it is :-(
After I filter and add the offset for same values, how do I update my array with these updated values?
The following method modifies in place the latitude of each store that matches the latitude of another store:
allStoresInfo.map{ currentStore in allStoresInfo.filter{$0.latLng![0] == currentStore.latLng![0]}.enumerated().forEach{ index, matchingStore in
matchingStore.latLng![0] += Double(index)*0.001
}
}
Just a small piece of advice: don't store lat-long values in an array. Either create a struct/class for them or use a tuple to store them.

dymamic Schemas and nested Maps in Avro

I'm new to Avro, and am trying to write some code to serialize some nested objects.
The structure of the objects looks like this:
class Parcel {
String recipe;
Map<Integer, PluginDump> dumps;
}
class PluginDump {
byte[] state;
Map<String, Param> params;
}
class Param {
Type type; //can be e.g. StringType, BooleanType, etc
Object value;
}
So I can't use a static avro schema - each PluginDump will have a different schema depending on the types within it.
I have written some code which can generate a Schema based on an individual PluginDump.
So when serializing a Parcel, how do I 'put' each PluginDump entry?
Here is my code:
Schema parcelSchema = AvroHelper.getSchema(p);
GenericRecord parcelRecord = new GenericData.Record(parcelSchema);
parcelRecord.put("recipe", p.getRecipe().toJson());
for (Map.Entry<Integer, PluginDump> entry : p.getDumps().entrySet()) {
PluginDump dump = entry.getValue();
Integer uid = entry.getKey();
Schema dumpSchema = AvroHelper.getSchema(dump);//will be different for each PluginDump
parcelRecord.put(????
Any ideas?
I have a feeling my approach is wrong, but I can't find any examples in the documentation of dynamic schema generation or nested maps.
1 When you get GenericRecord parcelRecord = new GenericData.Record(parcelSchema); you have two fields in your record: recipe and dumps, so you can't iterate through the dumps, you must put prepared map with dumps in the second field of record, just like you did it for recipe: parcelRecord.put("dumps", dumps);. But in this case, you'll get ClassCastException, because PluginDump cannot be cast to org.apache.avro.generic.IndexedRecord, so you need to put in parcelRecord a Map of GenericRecords. Also you need this for Map<String, Param> params, cause Param cannot be cast to IndexedRecord too.
2 Then, I think that its better to use Lists instead of Maps, cause avro not very good enough to work with Maps with different types of keys and values.
3 About the Param class: if you will use auto-generated schema, Param class will be presented like this.
"type": "record",
"name": "Param",
"fields": [
{
"name": "type",
"type": {
"type": "record",
"name": "Type",
"namespace": "java.lang.reflect",
"fields": []
}
},
{
"name": "value",
"type": {
"type": "record",
"name": "Object",
"namespace": "java.lang",
"fields": []
}
}
]
As far as avro uses java.lang.reflect, you will lose type field after deserialization, avro will not know what type it was.
If you want to generate avro-schema manually for each Param, considering its type, you can do something like this (I used ClassUtils.getClass from apache commons-lang3, cause standart Class.forName method doesn't always work properly):
public Schema getParamSchema() throws ClassNotFoundException {
List<Schema.Field> fields = new ArrayList<>();
fields.add(new Schema.Field("key", Schema.create(Schema.Type.STRING), "Doc: key field", (Object) null));
Schema.Field f = new Schema.Field("type", ReflectData.get().getSchema(ClassUtils.getClass(((Class) this.type).getName())), "Doc: type field", (Object) null);
f.addProp("java-class", ((Class) this.type).getName());
fields.add(f);
fields.add(new Schema.Field("value", ReflectData.get().getSchema(value.getClass()), "Doc: value field", (Object) null));
return Schema.createRecord(((Class) this.type).getName() + "Param", "Doc: param record", this.getClass().getPackage().getName(), false, fields);
}
But in this case, avro will throw ClassCastException, because it can't cast Class to Boolean, Integer etc. I always had a lot of problems working with avro and java Types and Classes.
So the best advice i think will be to change you model (Parcel, PluginDump and Param i mean) to have less problems with avro. For example you can store type name like a string, and get a Type with reflection after deserializing.

Retrieving data using Grails Domain returns with a Class Key by default

I'm working with a Grails query service and I'm using these the code blocks to retrieve database rows via a domain class.
adjustmentCodeList = AdjustmentCode.findAll {
or {
ilike('description', "%$filterText%")
like('id', "%$filterText%")
}
}
adjustmentCodeList = AdjustmentCode.list()
adjustmentCodeList = AdjustmentCode.list(max: count, offset: from)
It works fine actually, but there is a little problem though. It returns the following list (some sensitive data are omitted):
[
{
"class": "rvms.maintenance.AdjustmentCode",
"id": ...,
"description": ...,
"lastUpdateBy": ...,
"lastUpdateDate": ...,
"status": ...,
"statusDate": ...,
"type": ...
},
{
"class": "rvms.maintenance.AdjustmentCode",
"id": ...,
"description": ...,
"lastUpdateBy": ...,
"lastUpdateDate": ...,
"status": ...,
"statusDate": ...,
"type": ...
},
...
{
"class": "rvms.maintenance.AdjustmentCode",
"id": ...,
"description": ...,
"lastUpdateBy": ...,
"lastUpdateDate": ...,
"status": ...,
"statusDate": ...,
"type": ...
}
]
It includes the domain class name. How can I remove the class key using some config? My current solution is to manually remove the class key from the list by iterating it inside a loop, removing that key one at a time. But maybe... there is another Grails-ly way.
If you want to see the domain, it looks like this:
package rvms.maintenance
import grails.util.Holders
import groovy.sql.Sql
import oracle.jdbc.OracleTypes
import java.sql.Connection
class AdjustmentCode implements Serializable {
String id
String description
String type
String status
Date statusDate
String lastUpdateBy
Date lastUpdateDate
static mapping = {
table '...'
version false
id column : '...'
description column : '...'
type column : '...'
status column : '...'
statusDate column : '...'
lastUpdateBy column : '...'
lastUpdateDate column : '...'
}
Map getAdjustmentCodeValues() {
Map values = [];
values << [id: this.getId()]
values << [description: this.getDescription()]
values << [type: this.getType()]
values << [status: this.getStatus()]
values << [statusDate: this.getStatusDate()]
values << [lastUpdateBy: this.getLastUpdateBy()]
values << [lastUpdateDate: this.getLastUpdateDate()]
return values
}
}
The Grails way to accomplish this is to customize the marshaller. I've explained how to do this with named marshallers in this answer and the same concept applies to your case as well (minus the named portion).

Resources