Dart accessing class fields like map values - dart

I was wondering if it was possible to access the values of fields in an object with their names in a manner analogous to accessing values in a map with the key names. For example, something like this
void main() {
MyData d=MyData();
List<String> fieldNames=['a','b','c'];
for(var name in fieldNames){
print('This is the value of the $name field: ${d[name]}}');
}
}
class MyData{
String a='A';
String b='B';
String c='C';
}
Of course, this doesn't work because Dart doesn't quite know what to make of d[name] because d is an object. But if d was a map, it would work. Like this.
void main() {
Map d=myData;
List<String> fieldNames=['a','b','c'];
for(var name in fieldNames){
print('This is the value of the $name field: ${d[name]}}');
}
}
Map myData={
'a':'A',
'b':'B',
'c':'C',
};
So my question is this. If I have a class, is there any way to treat it like a Map in the situations where I might want to refer to several of the field values indirectly via their names like I tried to do above? Or is this sort of trick not possible in a compiled language like Dart?

The short answer is "no". The longer answer is "noooooooo". :)
But seriously, the namespace of your program code is very separate from the data values that are managed by your code. This isn't JavaScript, where we can freely flow from code to data to code again.

Related

Dart Generic Function with Subtype function call

I am not sure if this is even possible but here's my setup:
I have basically 2 Maps holding a special identifier to get some objects.
these identifier is like a versioning number, i may have data in version 8 that belongs to meta version 5. But at the same time, Meta versions up to 10 may exist and not every meta version holds information about every data, so here's where the _filter kicks in.
The filter is able to find to any given value the correct object. So far so good.
My question belongs to the following: (last codeline)
how am i able to say "if you have no matching candidate, generate me a default value"
For this purpose, i tried to force a named constructor with a super class for "Data" and "Meta" called "BasicInformation".
But even if i implement this, how do i call something like T.namedConstructor(); ?
class Repo{
Map<int, Data> mapData;
Map<int, Meta> mapMeta;
Data getData(int value)
{
return _filter<Data>(mapData, value);
}
Meta getMeta(int value)
{
return _filter<Data>(mapMeta, value);
}
T _filter<T extends BasicInformation>(Map<int, T>, int value)
{
//fancy filtering technique
//....
//speudo code
if (found) return map[found]; //speudo code
else return T.generateDefault();
}
}
I've found the following stackoverflow entry: Calling method on generic type Dart
which says, this is not possible without adding a function call.

How to get a property by this name in String?

I want to access to a property by the name si string format.
If I have a class like that:
class PrefsState {
String a;
PrefsState({
this.a,
})
How can I do something like that:
PrefsState test= PrefsState(a: "it is a test");
String key = "a";
print(test[key]);
Of course is not working. There is a way to do that in Dart ?
Unfortunately, you cannot use reflection/mirrors in flutter.
What you can do, which is tedious, is use maps.
class PrefsState {
String a;
const PrefsState({ this.a, });
dynamic getProp(String key) => <String, dynamic>{
'a' : a,
}[key];
}
It's probably better to build the map in the constructor, but if you want const constructors then you'll have to settle for this. Likely won't make much of a difference unless you have a million parameters anyway. Then you use it like so:
PrefsState test= PrefsState(a: "it is a test");
String key = "a";
print(test.getProp(key));
I don't think there is a less cumbersome way of doing this, but would love to be proven wrong :-)
You can do it with mirrors, but mirrors don't work in dart2js or flutter. You can use code builders to get at this, but the real question is what is your need for this?

Dataflow output parameterized type to avro file

I have a pipeline that successfully outputs an Avro file as follows:
#DefaultCoder(AvroCoder.class)
class MyOutput_T_S {
T foo;
S bar;
Boolean baz;
public MyOutput_T_S() {}
}
#DefaultCoder(AvroCoder.class)
class T {
String id;
public T() {}
}
#DefaultCoder(AvroCoder.class)
class S {
String id;
public S() {}
}
...
PCollection<MyOutput_T_S> output = input.apply(myTransform);
output.apply(AvroIO.Write.to("/out").withSchema(MyOutput_T_S.class));
How can I reproduce this exact behavior except with a parameterized output MyOutput<T, S> (where T and S are both Avro code-able using reflection).
The main issue is that Avro reflection doesn't work for parameterized types. So based on these responses:
Setting Custom Coders & Handling Parameterized types
Using Avrocoder for Custom Types with Generics
1) I think I need to write a custom CoderFactory but, I am having difficulty figuring out exactly how this works (I'm having trouble finding examples). Oddly enough, a completely naive coder factory appears to let me run the pipeline and inspect proper output using DataflowAssert:
cr.RegisterCoder(MyOutput.class, new CoderFactory() {
#Override
public Coder<?> create(List<? excents Coder<?>> componentCoders) {
Schema schema = new Schema.Parser().parse("{\"type\":\"record\,"
+ "\"name\":\"MyOutput\","
+ "\"namespace\":\"mypackage"\","
+ "\"fields\":[]}"
return AvroCoder.of(MyOutput.class, schema);
}
#Override
public List<Object> getInstanceComponents(Object value) {
MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
List components = new ArrayList();
return components;
}
While I can successfully assert against the output now, I expect this will not cut it for writing to a file. I haven't figured out how I'm supposed to use the provided componentCoders to generate the correct schema and if I try to just shove the schema of T or S into fields I get:
java.lang.IllegalArgumentException: Unable to get field id from class null
2) Assuming I figure out how to encode MyOutput. What do I pass to AvroIO.Write.withSchema? If I pass either MyOutput.class or the schema I get type mismatch errors.
I think there are two questions (correct me if I am wrong):
How do I enable the coder registry to provide coders for various parameterizations of MyOutput<T, S>?
How do I values of MyOutput<T, S> to a file using AvroIO.Write.
The first question is to be solved by registering a CoderFactory as in the linked question you found.
Your naive coder is probably allowing you to run the pipeline without issues because serialization is being optimized away. Certainly an Avro schema with no fields will result in those fields being dropped in a serialization+deserialization round trip.
But assuming you fill in the schema with the fields, your approach to CoderFactory#create looks right. I don't know the exact cause of the message java.lang.IllegalArgumentException: Unable to get field id from class null, but the call to AvroCoder.of(MyOutput.class, schema) should work, for an appropriately assembled schema. If there is an issue with this, more details (such as the rest of the stack track) would be helpful.
However, your override of CoderFactory#getInstanceComponents should return a list of values, one per type parameter of MyOutput. Like so:
#Override
public List<Object> getInstanceComponents(Object value) {
MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
return ImmutableList.of(myOutput.foo, myOutput.bar);
}
The second question can be answered using some of the same support code as the first, but otherwise is independent. AvroIO.Write.withSchema always explicitly uses the provided schema. It does use AvroCoder under the hood, but this is actually an implementation detail. Providing a compatible schema is all that is necessary - such a schema will have to be composed for each value of T and S for which you want to output MyOutput<T, S>.

Custom component attribute types

I was wondering if i can pass other types of arguments than int, String to custom components explained here.
My problem is that i want to make a component and i want to pass data of type List, especially List<Map<String, String>> to the component itself. How can i achive this functionality? In other words, how can i pass other arguments to a component in angulardart than int or String?
My related code looks like this:
#NgComponent(
...
)
class MyComponent {
List<Map<String, String>> myList;
...
#NgAttr('myAttr')
set setMyAttribute(List<Map<String, String>> myList) {
this.myList = myList;
}
...
}
Where i use the component:
<div>
...
<mycomponent myAttr="ctrl.returnsAListOfMaps"></mycomponent>
...
</div>
Any idea? Is it possible?
Cheers
With #NgAttr the literal value of the attribute is assigned to the fild.
With #NgOneWay, #NgTwoWay, #NgOneWayOneTime the attribute value is evaluated and the result gets assigned.
#NgCallback is for assigning callback functions.
It is possible to use many kind of types with component attributes (map, list and even object/class).
Here you can find a couple of examples: https://stackoverflow.com/a/21961449/2777805

Is there anything like "CheckSum" in Dart (on Objects)?

For Testing purposes I'm trying to design a way to verify that the results of statistical tests are identical across versions, platforms and such. There are a lot things that go on that include ints, nums, dates, Strings and more inside our collections of Objects.
In the end I want to 'know' that the whole set of instantiated objects sum to the same value (by just doing something like adding the checkSum of all internal properties).
I can write low level code for each internal value to return a checkSum but I was thinking that perhaps something like this already exists.
Thanks!
_swarmii
This sounds like you should be using the serialization library (install via Pub).
Here's a simple example to get you started:
import 'dart:io';
import 'package:serialization/serialization.dart';
class Address {
String street;
int number;
}
main() {
var address = new Address()
..number = 5
..street = 'Luumut';
var serialization = new Serialization()
..addRuleFor(address);
Map output = serialization.write(address, new SimpleJsonFormat());
print(output);
}
Then depending on what you want to do exactly, I'm sure you can fine tune the code for your purpose.

Resources