2

I have data from Firebase that looks something like this:

{
    "foo": {
        1: true,
        2: false
    },
    "bar": {
        3: true,
        4: false
    }
}

If I'm not mistaken, this should be a Map<String, Map<int, bool>>. I'm trying to use this function to safely cast it:

T castWithDefaultValue<T> (val, T defaultValue) => val is T ? val : defaultValue;

var castData = castWithDefaultValue<Map<String, Map<int, bool>>>(dataFromFirebase, {});

But it fails, always returning the default value. The only types I can get to work for either the key or the value are Object? and dynamic, but I want more type safety. How do I get it to what I want it to be?

John Montgomery
  • 6,739
  • 9
  • 52
  • 68

2 Answers2

1

When I have a snapshot with just string values, getting them is a two-step process:

var data = snapshot.val() as Map<String,dynamic>;
var values = Map<String, String>.from(data);

The first line is a regular cast, while the second line is a data conversion. Also see: Difference between List<String>.from() and as List<String> in Dart

I'm not sure if your conversion can be done for the entire object in one go, but if it can it'd be something like:

Map<String, Map<int, bool>>.from(dataFromFirebase)

That seems to work here at least: https://dartpad.dev/9dad7a0e39add2c690b3b54afe284005

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Seems like it only works with literal values unfortunately, otherwise it throws an exception on the inner maps. It also throws if the data is null or invalid, which is one of the problems I was trying to avoid with my initial attempt. – John Montgomery Jan 05 '22 at 00:06
  • "it throws an exception on the inner maps" What exception? For example, I'm not convinced your inner structure will show up as a `Map>` in all cases. Using sequential numeric keys is a bad idea on Firebase, because they may get interpreted as arrays. See [Best Practices: Arrays in Firebase](https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html). – Frank van Puffelen Jan 05 '22 at 00:39
  • [Like this](https://dartpad.dev/?id=806ab09ecc4a5dcc66fedd79d9afce6e). Changing the structure of the data isn't an option, but the keys in my actual code aren't sequential. – John Montgomery Jan 05 '22 at 00:55
1

I don't think you can cast a nested data structure like this. See this discussion for more info.

The best I think you can do is deep copy the data structure into a new collection and cast each part as you are copying. In this case you could do something like:

void main() {
  // intentionally removing type data for example
  dynamic data = <dynamic, dynamic>{
    "foo": <dynamic, dynamic>{1: true, 2: false},
    "bar": <dynamic, dynamic>{3: true, 4: false},
  };

  final deepCopy = {
    for (final entry in data.entries)
      entry.key as String: {
        for (final subEntry in entry.value.entries)
          subEntry.key as int: subEntry.value as bool,
      },
  };

  print(deepCopy);
  print(deepCopy.runtimeType);
}
mmcdon20
  • 5,767
  • 1
  • 18
  • 26