Insert new pair at index 0 to map. ( Dart ) - dart

I'm to loop this map. So I would like to be able to insert a pair into this map. How do I do it?
Map<String, String> fruits = {'Apple': 'Golden', 'Orange': 'Orange'};
fruits.insert(0, {'Grape': 'Green'}); // doesn't work
print(fruits); //{Grape: Green, Apple: Golden, Orange: Orange}

Map entries do not have numeric indices (Unless you're working with Map<int, T> of course), and there is no Map.insert method. Instead, the way to add a pair to a map is to assign a value to a key, like so:
fruits['Grape'] = 'Green';
For some additional info, Map is unordered, meaning there is no index 0 - BUT, if you really must have an ordered set of keys and values, you can use List<MapEntry>:
List<MapEntry<String, String>> fruits = [
MapEntry('Apple', 'Golden'),
MapEntry('Orange', 'Orange')
];
fruits.insert(0, MapEntry('Grape', 'Green'));
print(fruits); // [MapEntry(Grape: Green), MapEntry(Apple: Golden), MapEntry(Orange: Orange)]
Even better though, you might consider using a Fruit class, then having a List<Fruit>:
class Fruit {
final String name;
final String color;
Fruit(this.name, this.color});
}
Then:
List<Fruit> fruits = [
Fruit('Apple', 'Golden'),
Fruit('Orange', 'Orange')
];
fruits.insert(0, Fruit('Grape', 'Green'));
print(fruits);

Since the default implementation of a Map is a LinkedHashMap, which preserves the inserting order, you should be able to use this syntax to insert an element either at the start or at the end of a map.
Map<String, String> fruits = {'Apple': 'Golden', 'Orange': 'Orange'};
fruits = {'Grape': 'Green', ...fruits};
print(fruits); //{Grape: Green, Apple: Golden, Orange: Orange}
Be aware that you are actually creating a new Map rather then inserting the element in the existing one.

Related

How to find an object by a property from a List of List of objects in dart?

Hello I am new to dart and trying to find an item by property name in a list of list.
class Product{
String id;
String title;
Product(this.id,this.title);
}
void main(){
List<List<Product>> allProdcuts=[
//New Prodcuts
[
Product("1","Hammer"),
Product("3","Nails"),
Product("2","Screws"),
],
futureItems,
//Old Prodcuts
[
Product("4","Rock"),
Product("5","Paper"),
Product("6","Scissor"),
],
//Rare Items
[
Product("7","Plank"),
Product("8","Wires"),
Product("9","Box"),
],
];
print(allProdcuts.where((itemsList)=>itemsList.contains((product)=>product.title='Wires')));
//Returns ()
}
I have tried using for a single List:
List<Product> futureItems= [
Product("101","Galactic Hammer"),
Product("301","Galactic Nails"),
Product("201","Galactic Screws"),
];
print(newProduct.firstWhere((p)=>p.title=='Hammer'));
//Instance of 'Product'
Also tried this:
print(allProdcuts.map((itemList)=>itemList.firstWhere((p)=>p.title=='Nails')));
// Bad state: No elementError: Bad state: No element.
But there is an element with the title='Nails'.I don't understand what I am doing wrong.
You are calling itemList.firstWhere((p)=>p.title=='Nails') on each list, also the ones with no element with title "Nails". Since firstWhere throws if there is no matching value, it does that for two of your three lists. Also, in the example, itemsList.contains(...) does not take a callback, so you are just checking whether a function is in the list, which it isn't. You might want to use any for that, but it won't solve the problem here.
To do this efficiently, I'd probably create helper function:
Product findByTitle(List<List<Product>> allProducts, String title) {
for (var products in allProducts) {
for (var product in products) {
if (product.title == title) return product;
}
}
// Or return `null`.
throw ArgumentError.value(title, "title", "No element with that title");
}
The return in the middle allows you to skip out of the double iteration the moment you have a match, something which is harder to do with firstWhere/map/forEach etc.
One alternative solutions would be:
var product = allProducts.expand((l) => l.where((p) => p.title == title)).first;
which finds all the products with the given title and flattens them into a single iterable, then picks the first one (if there are any). Because iterables are lazy, it will actually stop at the first match.
There are many ways to solve this.
One example is to use the forEach() method:
allProdcuts.forEach(
(List<Product> l)=>l.forEach(
(Product p){
if (p.title=="Nails")
print(p.id);
}
)
);
The for each method receives a function and applies this function to every element on the list. If you have a lists of lists, you can do this twice to get a function applied to each element of the sub lists.
The above code prints 3, which is the desired result.
Another solution would be to flatten the list first, so you can have an easier search later.
print(allProdcuts.any((innerListOfProducts) =>
innerListOfProducts.any((product) => product.title == 'Wires')));
This code will return true if 'Wires' is in the inner list, and false otherwise.

Is it possible to make a TreeMap from a Map literal?

I have a Map literal, an I want it to be a TreeMap, but by default I believe it's a LinkedHashMap. Casting a LinkedHashMap to a TreeMap won't work as it's not a subtype.
Basically, I'm looking for the simplest way to make this work:
var map = <int, int>{for(int i = 0; i < intervals.length; i++) intervals[i][0] : i} as SplayTreeMap;
As mentioned before, casting as SplayTreeMap won't work as they types don't align.
Thanks much in advance
Use the SplayTreeMap.from constructor to create a SplayTreeMap. There isn't any way to cast it as you said.
Remove the as from your current code and add this to get your SplayTreeMap:
var newMap = SplayTreeMap.from(map);
Depending on your key type and your use case, you can pass compare and isValidKey parameters as well. Full constructor definition:
SplayTreeMap<K, V>.from(
Map other,
[int compare(
K key1,
K key2
),
bool isValidKey(
dynamic potentialKey
)]
)

Flutter sort Firebase snapshot by timestamp

I'm trying to sort snapshot by timestamp but returns original order.
data structure looks like this
I have two snapshot and timestamps are -1536025466539 and -1536025893015.
So, I expect -1536025893015 to come first after sorted.
Does anyone know how to sort correctly?
Code:
Map<dynamic, dynamic> map = snapshot.data?.snapshot?.value;
map.values.toList().sort((a, b) {
return a['timestamp'].compareTo(b['timestamp']);
// also not work return b['timestamp'].compareTo(a['timestamp']);
});
From the above code, it looks like you are not having a variable to hold the list.
Map<dynamic, dynamic> map = {
"one": {"timestamp": 1},
"two": {"timestamp": 2}
};
List list = map.values.toList(); //variable which holds new list created from Map.
//As it new list, any change in list will not have no impact on map.
list.sort((a, b) {
return b["timestamp"].compareTo(a["timestamp"]);
}); // inplace sort
print(list); // [{timestamp: 2}, {timestamp: 1}]
If you want key also in the result,
var list = map.entries.toList();
list.sort((a, b) => b.value["timestamp"].compareTo(a.value["timestamp"]));
var list2 = list.map((a) => {a.key: a.value}).toList();
print(list2); // [{two: {timestamp: 2}}, {one: {timestamp: 1}}]
Unfortunately when you call snapshot.data?.snapshot?.value, the resulting Map doesn't have space for the order of the items anymore.
In most Firebase SDKs you can solve this by looping over the children of the snapshot in the value event listener. But in Flutter you will need to listen to onChildAdded events to maintain the value of the items in Flutter, at least until this issue is addressed: https://github.com/flutter/flutter/issues/20745.
From one of my own projects:
ref.orderByKey().equalTo("child1").onChildAdded.listen((event) {
print(event.snapshot.key);
});
ref.child("child1").once().then((snapshot) {
print("Done loading all data for child1");
});
This will first print the keys of all child nodes (in the requested order), and then "Done loading all data for child1".

Easiest way to convert a string into a HashMap

if I have some text in a String like:
"abc=123,def=456,ghi=789"
how could I create a populated HashMap<String,Int> object for it in the easiest, shortest amount of code possible in Kotlin?
I can think of no solution easier than this:
val s = "abc=123,def=456,ghi=789"
val map = s.split(",").associate {
val (left, right) = it.split("=")
left to right.toInt()
}
Or, if you need exactly a HashMap, use .associateTo(HashMap()) { ... }.
Some details:
.associate { ... } receives a function that produces pairs which are then stored into a map as keys and values respectively.
val (left, right) = it.split("=") is the usage of destructuring declarations on the list returned from it.split("="), it takes the first two items from the list.
left to right.toInt() creates a Pair<String, Int> defining a single mapping.
You can map each key/value to a Pair with the to keyword. An iterable of Pair can be mapped to a Map easily with the toMap() extension method.
val s = "abc=123,def=456,ghi=789"
val output = s.split(",")
.map { it.split("=") }
.map { it.first() to it.last().toInt() }
.toMap()

Unable to assert that list contains a map

I want to use hamcrest to compare a list which has map entries.
Map<String, String> aMapWithCertainEntries = new HashMap();
aMapWithCertainEntries.put("entry1Key", "entry1Value");
aMapWithCertainEntries.put("entry2Key", "entry2Value");
List<Map<String,String>> listToTest = Arrays.asList(new Map[] {aMapWithCertainEntries});
//I want to assert that list has a map with entry1Key, entry2Key keys and corresponding values
assertThat(listToTest, hasItem(??))
System.out.println();
In the place marked ?? I want to create the right matcher to assert that my map contains specific keys and values.
Any help/pointers will be greatly appreciated.
If you simply want to assert that the List contains a Map whose entire contents are as expected, the easiest approach is:
Map<String, String> expectedEntries = ....;
assertThat(listToTest, hasItem(expectedEntries));
However, if you want to ensure that the List contains a Map which contains the given subset of entries, you will need to take one of the following approaches:
Approach 1: Create a Custom Matcher
assertThat(listToTest, hasItem(new CustomTypeSafeMatcher<Map<String,String>>("an entrySet that contains " + expectedEntries.entrySet()) {
#Override
protected boolean matchesSafely(Map<String, String> o) {
return hasItems(expectedEntries.entrySet().toArray()).matches(o.entrySet());
}
}));
Approach 2: Assert on each Map's entrySet rather than on the Maps themselves
assertThat(listToTest.stream().map(Map::entrySet).collect(Collectors.toList()),
hasItem(hasItems(expectedEntries.entrySet().toArray(new Map.Entry[expectedEntries.size()]))));

Resources