How to extract a pair of values out of a JSON using dart
This is the function I have so far:
This is the JSON:
{
"displayNames": [
"John Doe",
"ChloƩ Kamgang",
"Lady Testing of Tarth"
],
"users": [
{
"uid": "1tcPRqNZ7wexX2DWa11V9Ay1zES2",
"displayName": "John Doe"
},
{
"uid": "aZg7Emp8H9W42irnM73NBdTe6YM2",
"displayName": "ChloƩ Kamgang",
"photoURL": "https://firebasestorage.googleapis.com/v0/b/atalaku-fcf9e.appspot.com/o/Profilescaled_1e189635-0bc0-4791-ba20-6aded7ad3e8f7210544.jpg621jpg?alt=media&token=f5cffac3-a20c-4a83-8241-4fab16a9bd66"
},
{
"uid": "hXzyTuDE8eafnSxITmz7ZdMQDnw1",
"displayName": "Lady Testing of Tarth",
"photoURL": "https://firebasestorage.googleapis.com/v0/b/atalaku-fcf9e.appspot.com/o/Profilescaled_image_picker8162975920.jpg645jpg?alt=media&token=269007d1-09ee-4fe4-b7ad-72aa7cea756a"
}
]
}
And this is the function:
Future<List<Map<String, dynamic>>> getProfilesBySuggestion(
String sSuggestion) async {
try {
List<Map<String, dynamic>> listToReturn = [];
if (sSuggestion == null ||
sSuggestion.isEmpty ||
!sSuggestion.startsWith('#')) return listToReturn;
//Getting all the names
Map<String, dynamic> listUsers = await getAllUsers();
if (listUsers == null) return listToReturn;
//Returning only the names corresponding
List<dynamic> listNames = listUsers['displayNames'];
for (String name in listNames)
if (name
.toLowerCase()
.startsWith(sSuggestion.substring(1).toLowerCase())) {
List<Map<String, dynamic>> listOfusers = listUsers['users'] as List<Map<String, dynamic>>;
Map<String, dynamic> rightOne = listOfusers.firstWhere((user) => user['displayName'] == name);
String sPhotoURL = rightOne['photoURL'];
print('** name = $name, photoURL = $sPhotoURL');
listToReturn.add({'name': name, 'photoURL': sPhotoURL});
}
return listToReturn;
} catch (error) {
print('*** Error During getProfilesBySuggestion: ${error.toString()}');
return [
{'errorMessage': error.toString()}
];
}
}
What I want is a list of pair (name, photoURL). I am using the flutter_typeahead plugin, I want to display a list of names and their respective avatars. As you can see, I am using Firebase. Please let me know if there is a better option as well, as this will get pretty heavy with scale. Thank you!
A good starting point when working with data that's not organised the way you want it, is to re-organize it. I'd merge the two lists adding the display name to each user. But, of course, you see immediately that that's not necessary as each user already contains their display name. (The displayNames branch of your json is unnecessary for your purposes.)
So, you can just work with the users branch. Note that what you are doing is extracting the members of the users list and subtly changing the tag name (from displayName to name - leaving photoURL the same). Is the tag name change necessary? If not you could basically achieve what you want with a single where.
That said, this should achieve what you want (including the tag name change):
Future<List<Map<String, dynamic>>> getProfilesBySuggestion(
String sSuggestion) async {
try {
if (sSuggestion == null ||
sSuggestion.isEmpty ||
!sSuggestion.startsWith('#')) return [];
//Getting all the names
Map<String, dynamic> allUsers = await getAllUsers();
if (allUsers == null) return [];
//Returning only the names corresponding
List<dynamic> users = allUsers['users'];
var suffix = sSuggestion.substring(1).toLowerCase();
return users
.where((user) => user['displayName'].toLowerCase().startsWith(suffix))
.map<Map<String, dynamic>>((user) => {
'name': user['displayName'],
'photoURL': user['photoURL'],
})
.toList();
} catch (error) {
print('*** Error During getProfilesBySuggestion: ${error.toString()}');
return [
{'errorMessage': error.toString()}
];
}
}
A good starting point when working with data that's not organised the way you want it, is to re-organize it.
And if that's not an option, you can still mitigate the impact. It's always easier to work with plain old dart classes than json, so I'd encourage you to map the json to classes.
I usually use package:built_value for that. There is a tool that helps map json:
https://charafau.github.io/json2builtvalue/ Careful, it's not 100%. Eg. it will not include photoUrl field if you just copy-paste your json.
Basically, I'd take the json.users, and map it to objects. Make sure to make photoUrl optional.
Once that's done, you can create any lookup table you want. You could just have a List<User> to iterate over names and photoUrls, or you could create a Map<String, User> to look up users by their name.
Tip: class Map has got a number of constructors that you can work with.
Related
I want submit Selected answers array how can do this.
How to pass selected answer in this array
Flutter/Dart doesn't have arrays. Use lists instead.
Solution
In the api-response ,it's always formatted into either List of Map or Map of List.
List of Map is Like :
[
{
"name":"ruby",
"gender":"female"
},
{
"name":"otis",
"gender":"male"
},
]
And, Map of List is Like :
{
"data": [
"Professor",
"Berlin",
"Tokyo"
]
}
So to access them you have to use JsonDecode and then look the response and process that.
For Map response ....
var resMap = jsonDecode(response.body);
For List response (use key after resMap with key)...
var resMap = jsonDecode(response.body);
var infoList = resMap["data"];
Demo
class CatTypeController {
Future<void> getLeaves() async {
String url = "https://meowfacts.herokuapp.com/?count=2";
http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
Map rM = jsonDecode(response.body);
print(rM["data"]);
}
}
}
Response
Output
["In ancient Egypt, killing a cat was a crime punishable by death.","The way you treat kittens in the early stages of it's life will render it's personality traits later in life."]
It's the first time I use Dart and I'm stuck with a simple thing.
I have a simple Map and I need to remove some items from this map and modify the content.
I have this:
Map<String, List<String>> dataset = {
'apple': ['apple1', 'apple2', 'apple3'],
'pear': ['pear1', 'pear2'],
'ananas': ['ananas1', 'ananas2', 'ananas3'],
'orange': ['orange1', 'orange2', 'orange3', 'orange4'],
};
List<Map<dynamic, String>> fruits = [
{'key': 'pear', 'labelToShow': 'Pear fruit'},
{'key': 'ananas', 'labelToShow': 'My ananas'},
];
and I would like to have this:
Map<String, Map<String, List<String>>> result = {
'pear': {
'values': ['pear1', 'pear2'],
'labelToShow': 'Pear fruit'
},
'ananas': {
'values': ['ananas1', 'ananas2', 'ananas3'],
'labelToShow': 'My ananas'
},
};
So, basically, I need to remove from dataset the items that have the key that it's not included in fruits (in field key) and then I need to add the field labelToShow.
I dont' know ho to do that.
I started removing items from dataset doing so:
dataset.removeWhere((k, v) => k != 'pear' && k != 'ananas');
but I don't like, I would like to loop through fruits.
Can someone please help me?
Thanks a lot
I wouldn't remove anything from dataset. Instead I'd build a new map from scratch, with just the data you want.
How about:
Map<String, Map<String, List<String>>> result = {
for (var fruit in fruits)
fruit["key"]: {
"values": dataset[fruit["key"]],
"labelToShow": fruit["labelToShow"]
}
};
I have subjects collection. In this collection every document has tutors field that is object where key is id of tutors( from tutors collection)
tutors: {
"tutor_id_1": {
"name": "jonas",
"email": "jonas#gmail.com"
},
"tutor_id_2":{
"name": "stephen",
"email": "stephen#gmail.com"
},
"tutor_id_3":{
"name": "maria",
"email":"maria#gmail.com"
}
}
So how to query subjects where tutors field contain tutor id equal to "tutor_id_1" ?
I found one way
if I have two variables in client side
const tutorToFindId = "xxx"
const tutorToFindEmail = "YYY"
query(
collection(db, 'subjects'),
where(`tutors.${tutorToFindId}.email`, '==', `${tutorToFindEmail}`)
),
Is there any other way ??
As I understand, "tutor_id_1" is being used as a unique id. Considering that, you may structure your data model with "tutors" as a subcollection instead of a field and you will be able to get the content of that specific document as follows:
const docRef = db.collection('subjects').doc('subjectX').collection('tutors').doc(tutorToFindId);
const tutor = await docRef.get();
if (!tutor.exists) {
console.log('No such document!');
} else {
console.log('Document data:', tutor.data());
}
db.getBy({ where: { [`tutors.${tutorToFindId}.email`]: tutorToFindEmail } });
Take a look at the getBy function in https://www.npmjs.com/package/firebase-firestore-helper
Disclaimer: I am the creator of this library. It helps to manipulate objects in Firebase Firestore (and adds Cache)
Enjoy!
I have code like this for QuerySnapshot to get documents in collection that works good.
List<DaftarHazard> _userHazardFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((hazards) {
return DaftarHazard(
waktuHazard: hazards.data['waktuHazard'],
judulHazard: hazards.data['judulHazard'],
isiHazard: hazards.data['isiHazard'],
fotoHazard: hazards.data['fotoHazard'],
namaPelapor: hazards.data['namaPelapor'],
lokasiHazard: hazards.data['lokasiHazard'],
statusHazard: hazards.data['statusHazard']);
}).toList();
}
Stream<List<DaftarHazard>> get userHazardAsSuper {
return userHazards.snapshots().map(_userHazardFromSnapshot);
}
But then I need to Map DocumentSnapshot to class properties.
I don't know how to get this done.
How can I do this using the same method that I use for the QuerySnapshot?
The code above I get from collections.
Code below is the one I use to get fields in a document
Stream<List<UserDetails>> get userDetails {
return userData.document(uid).snapshots().map(_userDetailsFromSnapshot);
}
and using this to map to the properties
List<UserDetails> _userDetailsFromSnapshot(DocumentSnapshot snapshot) {
return snapshot.data.map((item) {
return UserDetails(
name: details.['name'],
email: details.data['email'],
);
}).toList();
}
But code above does not work.
Found the solutions here:
Net Ninja
You to extract the data from your DocumentSnapshot.
List<DaftarHazard> _userHazardFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((hazards) {
return DaftarHazard(
waktuHazard: hazards.data['waktuHazard'] != null ? WaktuHazard.fromData(hazards.data['waktuHazard'].data) : null,
judulHazard: hazards.data['judulHazard'],
isiHazard: hazards.data['isiHazard'],
fotoHazard: hazards.data['fotoHazard'],
namaPelapor: hazards.data['namaPelapor'],
lokasiHazard: hazards.data['lokasiHazard'],
statusHazard: hazards.data['statusHazard']);
}).toList();
}
And replace each property for its own object instead of DocumentSnapshots.
I'm not sure of the structure that you want, but basically, you just need to access the data property of your DocumentSnapshot and parse it accordingly.
Found the solution for getting field and value from a specific document in firestore here
[Net Ninja][1]
The following transaction completely replaces the data in a Firestore Map when run:
DocumentSnapshot docSnapshot = await tx.get(docRef); // doc Snapshot
if (docSnapshot.exists) {
await tx.update(
docRef,
<String, dynamic>{
'Users': {
'user.uid': {'name': user.displayName, 'date': DateTime.now()},
}
},
);
What would be the correct way to add to a map instead of replacing the existing data, the same way FieldValue.arrayUnion works?
Since you already fetched the data you could take the map out from the snapshot, replace the data there and call the update with the altered map.