I have this map:
var temp= {
'A' : 3,
'B' : 1,
'C' : 2
};
How to sort the values of the map (descending). I know, I can use temp.values.toList()..sort().
But I want to sort in context of the keys like this:
var temp= {
'B' : 1,
'C' : 2
'A' : 3,
};
This example uses a custom compare function which makes sort() sort the keys by value. Then the keys and values are inserted into a LinkedHashMap because this kind of map guarantees to preserve the order.
Basically the same as https://stackoverflow.com/a/29629447/217408 but customized to your use case.
import 'dart:collection';
void main() {
var temp= {
'A' : 3,
'B' : 1,
'C' : 2
};
var sortedKeys = temp.keys.toList(growable:false)
..sort((k1, k2) => temp[k1].compareTo(temp[k2]));
LinkedHashMap sortedMap = new LinkedHashMap
.fromIterable(sortedKeys, key: (k) => k, value: (k) => temp[k]);
print(sortedMap);
}
Try it on DartPad
The SplayTreeMap has a named constructor which accepts map and a comparator which is used to sort given map while building new map. Since SplayTreeMap is a descendant of Map you can easily substitute it.
import 'dart:collection';
void main() {
var unsorted = {'A': 3, 'B': 1, 'C': 2};
final sorted = SplayTreeMap.from(
unsorted, (key1, key2) => unsorted[key1].compareTo(unsorted[key2]));
print(sorted);
}
final Map<String, ClassCategory> category;
...
Map<String, ClassCategory> sorted = SplayTreeMap.from(category,
(key1, key2) => category[key1]!.title.compareTo(category[key2]!.title));
for (var item in sorted.entries) {
...
}
Related
This question already has answers here:
How to convert a List<T?> to List<T> in null safe Dart?
(2 answers)
Closed 3 months ago.
Is there a more elegant way of removing nulls from a Dart list than this:
List<T> nullFilter<T>(List<T?> list) =>
list.where((T? e) => e != null)
// This should not be necessary
.map((e) => e!)
.toList();
Something like this makes it a bit more clean:
List<T> nullFilter<T>(List<T?> list) => [...list.whereType<T>()];
void main() {
final list1 = [1, 2, 3, null];
print('${list1.runtimeType}: $list1');
// List<int?>: [1, 2, 3, null]
final list2 = nullFilter(list1);
print('${list2.runtimeType}: $list2');
// List<int>: [1, 2, 3]
}
You could use the very popular collection package (which is an official core package published by the Dart Team) as such:
final list2 = list1.whereNotNull();
Or as pointed out by the comment, if you don't want it as an iterable:
final list2 = list1.whereNotNull().toList();
For reference, the implementation for that is as follows (if you for some reason don't want to include the package but create the extension yourself in your own file):
/// Extensions that apply to iterables with a nullable element type.
extension IterableNullableExtension<T extends Object> on Iterable<T?> {
/// The non-`null` elements of this `Iterable`.
///
/// Returns an iterable which emits all the non-`null` elements
/// of this iterable, in their original iteration order.
///
/// For an `Iterable<X?>`, this method is equivalent to `.whereType<X>()`.
Iterable<T> whereNotNull() sync* {
for (var element in this) {
if (element != null) yield element;
}
}
}
Suppose I have these maps:
Map<int,List<String>> firstMap = {1:["a", "b"]};
Map<int,List<String>> secondMap = {2:["c"]};
Map<int,List<String>> thirdMap = {1:["d"]};
I want to merge them without overwriting values with same key to have this output:
{1: [a, b, d], 2: [c]
I used both spread operator and adAll method and both overwrite the value for key 1 to have {1: [d], 2: [c]}
instead of {1: [a, b, d], 2: [c].
void main() {
Map<int, List<String>> firstMap = {1: ["a", "b"]};
Map<int, List<String>> secondMap = {2: ["c"]};
Map<int, List<String>> thirdMap = {1: ["d"]};
var mergedMap = <int, List<String>>{};
for (var map in [firstMap, secondMap, thirdMap]) {
for (var entry in map.entries) {
// Add an empty `List` to `mergedMap` if the key doesn't already exist
// and then merge the `List`s.
(mergedMap[entry.key] ??= []).addAll(entry.value);
}
}
print(mergedMap); // Prints: {1: [a, b, d], 2: [c]}
}
I have a list that's formatted like {0:"a", 1:"b", 2:"c", 3: "a"} and want to remove dublicate values so that I end up with {0:"a", 1:"b", 2:"c"}. I'm also fine with geting {1:"b", 2:"c", 3: "a"}. What's the most idiomatic way to do this filtering?
package:quiver provides a bidirectional map (BiMap) that makes it an error to add elements that don't have unique values.
Alternatively, one easy way to filter duplicate values is to create a separate Set of values and use it to rebuild the Map:
Map<K, V> removeDuplicateValues<K, V>(Map<K, V> map) {
var valuesSoFar = <V>{};
return {
for (var mapEntry in map.entries)
if (valuesSoFar.add(mapEntry.value)) mapEntry.key: mapEntry.value,
};
}
void main() {
var map = {0: "a", 1: "b", 2: "c", 3: "a"};
print(removeDuplicateValues(map)); // Prints: {0: a, 1: b, 2: c}
}
If you want to change an existing map, you can do the following. I have made it as a extension method but you can of course also just have it as a separate method or some lines of codes:
extension RemoveDuplicateValuesExtension<K, V> on Map<K, V> {
void removeDuplicateValues() {
final valuesSoFar = <V>{};
this.removeWhere((_, value) => !valuesSoFar.add(value));
}
}
void main() {
final map = {0: "a", 1: "b", 2: "c", 3: "a"}..removeDuplicateValues();
print(map); // Prints: {0: a, 1: b, 2: c}
}
I have a dart list:
List<String?> vals;
I want to remove any null values and convert it to a List<String>.
I've tried:
List<String> removeNulls(List<String?> list) {
return list.where((c) => c != null).toList() as List<String>;
}
At run time I'm getting the following error:
List<String?>' is not a subtype of type 'List<String>?'
What is the correct way to resolve this?
Ideally you'd start with a List<String> in the first place. If you're building your list like:
String? s = maybeNullString();
var list = <String?>[
'foo',
'bar',
someCondition ? 'baz' : null,
s,
];
then you instead can use collection-if to avoid inserting null elements:
String? s = maybeNullString();
var list = <String?>[
'foo',
'bar',
if (someCondition) 'baz',
if (s != null) s,
];
An easy way to filter out null values from an Iterable<T?> and get an Iterable<T> result is to use .whereType<T>(). For example:
var list = <String?>['foo', 'bar', null, 'baz', null];
var withoutNulls = list.whereType<String>().toList();
Another approach is to use collection-for with collection-if:
var list = <String?>['foo', 'bar', null, 'baz', null];
var withoutNulls = <String>[
for (var s in list)
if (s != null) s
];
Finally, if you already know that your List doesn't contain any null elements but just need to cast the elements to a non-nullable type, other options are to use List.from:
var list = <String?>['foo', 'bar', 'baz'];
var withoutNulls = List<String>.from(list);
or if you don't want to create a new List, Iterable.cast:
var list = <String?>['foo', 'bar', 'baz'];
var withoutNulls = list.cast<String>();
Without creating a new List
void main() {
List<String?> list = ['a', null, 'b', 'c', null];
list.removeWhere((e) => e == null); // <-- This is all you need.
print(list); // [a, b, c]
}
Creating a new List
First create a method, filter for example:
List<String> filter(List<String?> input) {
input.removeWhere((e) => e == null);
return List<String>.from(input);
}
You can now use it:
void main() {
List<String?> list = ['a', null, 'b', 'c', null];
List<String> filteredList = filter(list); // New list
print(filteredList); // [a, b, c]
}
To use retainWhere, replace the predicate in removeWhere with (e) => e != null
Is there any way to have a foreach outside a function body for ex. generating code.
My scenario is that I have an associative array at compile-time that I need to use to generate specific fields. Unfortunately I can't really use a foreach outside of a function body to generate the members.
Right now I am using a work-around where I have a few mixins where I give one AA to the first mixin and that mixin converts the AA to an array and passes it to the second mixin, the second mixin is recursive with itself by ng itself until there is no more member, while also calling the first member in the array, which calls a third mixin that I can use to generate code.
It's not as smooth and dynamic as I really would want it to be and I was wondering if anyone has a better solution.
Here is my solution
// First mixin that takes the associative array and a string for the mixin to handle the items
mixin template StaticForEachAA(TKey,TValue, TKey[TValue] enumerator, string itemMixin) {
enum array = arrayAA!(TKey,TValue)(enumerator); // Converts the AA to an array of key-value pair structs
alias ArrayType = KeyValuePair!(TKey,TValue);
mixin StaticForEachArray!(ArrayType, array, itemMixin); // Calls second mixin with the array
}
// The second mixin that "fake loops" the array
mixin template StaticForEachArray(T, T[] array, string itemMixin) {
static if (array.length) {
import std.string : format;
mixin(format("mixin %s!(T, array[0]);", itemMixin)); // Mixins the itemMixin to handle the current item
mixin StaticForEachArray!(T, array[1 .. $], itemMixin); // slices the array to remove the item we just handled
}
}
// The third mixin that can be used to do whatever has to be done with item
mixin template StaticForEachItem(T, T item) {
import std.conv : to;
pragma(msg, to!string(item));
}
And to do the "fake foreach" for an associative
enum AA = [0 : 1, 1 : 2, 2 : 3];
mixin StaticForEachAA!(int, int, AA, "StaticForEachItem");
This will print the key-value pairs from AA at compile-time.
Leveraging the power of compile-time functione excution (CTFE) you could make a helper function that generates code for you using the data from an associative array (AA) you provide.
import std.string : format;
string generateCodeForEachAA(TKey, TValue)(TValue[TKey] data, string foreachBody)
{
string result;
foreach(k, v ; data)
{
result ~= format(foreachBody, k, v);
}
return result;
}
This function turns the given AA data into a string by formatting the given foreachBody with each AA element. The returned string can then be mixin'ed:
enum Data = [ 0 : 1, 1 : 2, 2 : 3 ];
enum Code = generateCodeForEachAA(Data, q{ pragma(msg, "%1$s => %2$s"); });
pragma(msg, "Code: " ~ Code);
mixin(Code);
Output:
Code: pragma(msg, "0 => 1"); pragma(msg, "1 => 2"); pragma(msg, "2 => 3");
0 => 1
1 => 2
2 => 3
Using this to generate members within a struct:
struct Foo
{
enum Members = [ "foo": 123, "bar": 42 ];
mixin(generateCodeForEachAA(Members, q{ typeof(%2$s) %1$s = %2$s; }));
}
void main()
{
import std.stdio : writeln;
Foo f;
writeln("foo: ", f.foo);
writeln("bar: ", f.bar);
}
Output:
foo: 123
bar: 42