Creating sum, min, max properties on iterators in Dart - dart

I have the following, which works for ints:
extension IterableInt on Iterable<int> {
int get max => reduce(math.max);
int get min => reduce(math.min);
int get sum => reduce((a, b) => a + b);
}
I wanted to make this more general to include decimals and created this:
extension IterableNum on Iterable<num> {
num get max => reduce(math.max);
num get min => reduce(math.min);
num get sum => reduce((a, b) => a + b);
}
These mostly fail, using the following tests:
void main(List<String> args) {
print([1.2, 1.3, 5, 2.2].sum); //works
print([1.2, 1.3, 5, 2.2].max); //works
print([1.2, 1.3, 5, 2.2].min); //works
print([1.2, 1.3, 5.0, 2.2].sum); //does not work
print([1, 2, 3].max); //does not work
print([1, 2, 3].min); //does not work
print([1, 2, 3].sum); //does not work
}
The error returned for the decimal case is:
type '(num, num) => num' is not a subtype of type '(double, double) => double' of 'combine'
It's similar for int, referring to int instead of double in the message.
The pattern seems to be that if there is a mixture of ints and decimals in the list then it's fine. If it's all ints or all decimals, it fails.
I've redefined the properties (and added multiply) using fold and this works:
extension IterableNum on Iterable<num> {
num get max => fold(first, math.max);
num get min => fold(first, math.min);
num get sum => fold(0, (a, b) => a + b);
num get multiply => fold(1, (a, b) => a * b);
}
Can someone explain why the first version of IterableNum (using reduce) does not work?

The issue is that reduce is very picky about its parameter function.
A List<int>'s reduce requires a function of type int Function(int, int). Nothing else will suffice. If you cast this List<int> to List<num>, you have something which seems like it expects to get a num Function(num, num), but actually requires an int Function(int, int). That's very hard to satisfy both of these (you need an int Function(num, num), which (a, b) => a + b is not.
(This is all because Dart class generics is unsafely covariant. A List<int> is always considered a subtype of List<num>, even though some of the functions aren't actually usable at List<num>).
Use fold is a good solution. It allows you to specify a return type different from the element type. It also requires you to have a value of that type to begin with, which is why it isn't always possible to use fold instead of reduce.

It does not work because you create lists of higher levels (subclasses of num type).
Your (extension) code should be more generic (more universal).
Like this code: extension IterableNum<T extends num> on Iterable<T>.
void main(List<String> args) {
print([1.2, 1.3, 5, 2.2].runtimeType);
print([1.2, 1.3, 5.0, 2.2].runtimeType);
}
Result:
List<num>
List<double>
Correct code as follow:
import 'dart:math' as math;
void main(List<String> args) {
print([1.2, 1.3, 5, 2.2].runtimeType);
print([1.2, 1.3, 5.0, 2.2].runtimeType);
print([1.2, 1.3, 5, 2.2].sum); //works
print([1.2, 1.3, 5, 2.2].max); //works
print([1.2, 1.3, 5, 2.2].min); //works
print([1.2, 1.3, 5.0, 2.2].sum); // WORKS!!!
print([1, 2, 3].max); // WORKS!!!
print([1, 2, 3].min); // WORKS!!!
print([1, 2, 3].sum); // WORKS!!!
}
extension IterableNum<T extends num> on Iterable<T> {
T get max => reduce(math.max);
T get min => reduce(math.min);
T get sum => reduce((a, b) => a + b as T);
}
Result:
List<num>
List<double>
9.7
5
1.2
9.7
3
1
6

Related

Dart map to another map through map function

Suppose I have a Map:
Map<String, int> source = {'a':1, 'b':2, 'c':3};
I want to get this:
Map<String, int> expected = {'a': 1, 'b':4, 'c':9 };
I want to achieve the result using map function:
Map<String,int> actual = source.map((key,value)=> {key: value * value});
However, I got this error:
The return type 'Map<String, int>' isn't a 'MapEntry<String, int>', as required by the closure's context
Can't we use the map function of map to get another map like this?
The mapping method should return a MapEntry instance since you can change both the key and value. So your code should instead be something like:
void main() {
final source = {'a': 1, 'b': 2, 'c': 3};
final actual = source.map((key, value) => MapEntry(key, value * value));
print(actual); // {a: 1, b: 4, c: 9}
}
Using collection-for might be more straightforward than using Map.map:
final source = {'a': 1, 'b': 2, 'c': 3};
final actual = {
for (var entry in source.entries)
entry.key: entry.value * entry.value,
};

Dart - get the nearest(larger) value from List?

How can I find the closest value in a list, which will return me the higher value?
Example: List of [3,7,12,19] if my value is 8 how can I get the nearest(larger) value 12? i want this logic in dart.
Just filter the List only for the values higher or equal to your number and get the lowest value:
var n = 8; // Number to match
var l = [3, 7, 12, 19]; // List of values
var greater = l.where((e) => e >= n).toList()..sort(); //List of the greater values
print(greater.first); // Print the first value. -> 12
To get Closest Value of number
import 'dart:math';
import 'dart:collection';
void main(){
List<double> value = [1,4,6,3,7,9,12,34,12,-12,-91];
print(value.getCloseValue(8)); // 7
print(value.getCloseValue(6)); // 6
print(value.getCloseValue(-11)); // -12
}
extension on List<num> {
num getCloseValue(num x) {
if (isEmpty) return 0;
Map<num, num> values = {};
forEach((e) {
values[e] = (e - x).abs();
});
var sortedKeys = values.keys.toList(growable:false)
..sort((k1, k2) => values[k1]!.compareTo(values[k2]!));
final sortedMap = LinkedHashMap
.fromIterable(sortedKeys, key: (k) => k, value: (k) => values[k]);
return sortedMap.keys.first;
}
}
List<int> arr = [6, 12, 11, 18, 24,5,6,99,10,9];
arr.sort((a, b) => a.compareTo(b));
print(arr);
print(Utils.getNextLargerNumber(8, arr));
and below is the logic:
static int getNextLargerNumber(int number, List<int> array)
{
for (var i = 0; i < array.length; i++) {
if (number < array[i]) {
return array[i];
}
}
return -1;
}
Mattia's answer is already good enough. (although the list cant have the length 0 and it might be not as efficient, as you have a where() as well as sort() in there). Here is a different approach, that solves those concerns:
Nearest value to target (larger favored)
final nearestLarger = list.isEmpty ? null : list.reduce(
(a, b) => (a-target).abs() < (b -target).abs() ? a : b);
Nearest value to target (smaller favoured)
final nearestSmaller = list.isEmpty ? null : list.reduce(
(a, b) => (a-target).abs() <= (b -target).abs() ? a : b);
Note that both functions retrieve the nearest value to the target, but in case of ambiguity (eg. [3,4,5]) either the bigger or smaller value is favored.

Is there anything like a struct in dart?

In javascript it always bothered me people use objects as vectors like {x: 1, y: 2} instead of using an array [1,2]. Access time for the array is much faster than the object but accessing by index is more confusing especially if you need a large array. I know dart has fixed arrays but is there a way to name the offsets of an array like you would a struct or a tuple/record in another language? Define enum/constants maybe?
I'd want something like
List<int> myVector = new List([x,y]);
myVector.x = 5;
is there an equivalent or idiomatic way to do this?
That sounds like a class.
class MyVector {
int x;
int y;
MyVector(this.x, this.y);
}
There is no simpler and more efficient way to create a name-indexed structure at runtime. For simplicity you could usually use a Map, but it's not as efficient as a real class.
A class should be at least as efficient (time and memory) as a fixed length list, after all it doesn't have to do an index bounds check.
In Dart 3.0, the language will introduce records. At that point, you can use a record with named fields instead of creating a primitive class:
var myVector = (x: 42, y: 37);
print(myVector.x);
A record is unmodifiable, so you won't be able to update the values after it has been created.
For me, i see 2 way to do this. I will sort by best in my point of view
Class based method
Here, the approach is to encapsulate your need, in a dedicated object
Pros:
It's encapsultate
You can propose several way to access variable, depend of the need
You can extend functionality without break everything
I love it :p
Cons
More time spend to create class, etc.
Do you really need what i say in pros ?
Maybe weird for js people
example :
class Vector {
int x;
int y;
static final String X = "x";
static final String Y = "y";
Vector({this.x, this.y});
Vector.fromList(List<int> listOfCoor) {
this.x = listOfCoor[0];
this.y = listOfCoor[1];
}
// Here i use String, but you can use [int] an redefine static final member
int operator[](String coor) {
if (coor == "x") {
return this.x;
} else if (coor == "y") {
return this.y;
} else {
// Need to be change by a more adapt exception :)
throw new Exception("Wrong coor");
}
}
}
void main() {
Vector v = new Vector(x: 5, y: 42);
Vector v2 = new Vector.fromList([12, 24]);
print(v.x); // print 5
print(v["y"]); // print 42
print(v2.x); // print 12
print(v2[Vector.Y]); // print 24
}
Enum based method:
You can also defined a "enum" (actually not really implement but will be in the future version) that will contains "shortcut" to your value
Pros
More simple to implement
Is more like your example ;p
Cons
Less extendable
i think is not very pretty
Not OOP think
example:
class Vector {
static final int x = 0;
static final int y = 1;
}
void main() {
List<int> myVector = new List(2);
myVector[Vector.x] = 5;
myVector[Vector.y] = 42;
}
Make your choice ;p
This is only possible with a class in Dart.
There are some open feature requests at http://dartbug.com
introduce struct (lightweight class)
Give us a way to structure Bytedata
If you have reasonably big data structure, you can use "dart:typed_data" as a model and provide lightweight view for the stored data. This way the overhead should be minimal.
For example, if you need 4X4 matrix of Uint8 values:
import "dart:typed_data";
import "dart:collection";
import "package:range/range.dart";
class Model4X4Uint8 {
final Uint8List _data;
static const int objectLength = 4 * 4;
final Queue<int> _freeSlotIndexes;
Model4X4Uint8(int length): _data = new Uint8List((length) * objectLength),
_freeSlotIndexes = new Queue<int>.from(range(0, length));
int get slotsLeft => _freeSlotIndexes.length;
num operator [](int index) => _data[index];
operator []=(int index, int val) => _data[index] = val;
int reserveSlot() =>
slotsLeft > 0 ? _freeSlotIndexes.removeFirst() : throw ("full");
void delete(int index) => _freeSlotIndexes.addFirst(index);
}
class Matrix4X4Uint8 {
final int offset;
final Model4X4Uint8 model;
const Matrix4X4Uint8(this.model, this.offset);
num operator [](int index) => model[offset + index];
operator []=(int index, int val) => model[offset + index] = val;
void delete() => model.delete(offset);
}
void main() {
final Model4X4Uint8 data = new Model4X4Uint8(100);
final Matrix4X4Uint8 mat = new Matrix4X4Uint8(data, data.reserveSlot())
..[14] = 10
..[12] = 256; //overlow;
print("${mat[0]} ${mat[4]} ${mat[8]} ${mat[12]} \n"
"${mat[1]} ${mat[5]} ${mat[9]} ${mat[13]} \n"
"${mat[2]} ${mat[6]} ${mat[10]} ${mat[14]} \n"
"${mat[3]} ${mat[7]} ${mat[11]} ${mat[15]} \n");
mat.delete();
}
But this is very low level solution and can easily create sneaky bugs with memory management and overflows.
You could also use an extension on List to create aliases to specific indexes.
Although it will be difficult to set up mutually exclusive aliases, in some cases, it may be a simple solution.
import 'package:test/test.dart';
extension Coordinates<V> on List<V> {
V get x => this[0];
V get y => this[1];
V get z => this[2];
}
void main() {
test('access by property', () {
var position = [5, 4, -2];
expect(position.x, 5);
expect(position.y, 4);
expect(position.z, -2);
});
}
The Tuple package https://pub.dev/packages/tuple might be what you are looking for when a class is too heavy.
import 'package:tuple/tuple.dart';
const point = Tuple2<int, int>(1, 2);
print(point.item1); // print 1
print(point.item2); // print 2

How can I filter a map / access the entries

The Map interface doesn't seem to provide access to the entries as an iterable, nor does it expose a where method to filter entries. Am I missing something? Is there a simple workaround?
e.g.
Map map;
final filteredMap = map.where((k, v) => k.startsWith("foo"));
Update: with control flow collection statements you can also do this:
final filteredMap = {
for (final key in map.keys)
if (!key.startsWith('foo')) key: map[key]
};
Original answer: Dart 2.0.0 added removeWhere which can be used to filter Map entities. Given your example, you could apply this as:
Map map;
final filteredMap = Map.from(map)..removeWhere((k, v) => !k.startsWith("foo"));
It's not the where method you asked for, but filtering Map entities is certainly doable this way.
Since Dart 2.0 Maps have an entries getter that returns an Iterable<MapEntry<K, V>> so you can do:
MapEntry theOne = map.entries.firstWhere((entry) {
return entry.key.startsWith('foo');
}, orElse: () => MapEntry(null, null));
You can use
library x;
void main(List<String> args) {
Map map = {'key1': 'aölsjfd', 'key2': 'oiweuwrow', 'key11': 'oipoip', 'key13': 'werwr'};
final filteredMap = new Map.fromIterable(
map.keys.where((k) => k.startsWith('key1')), key: (k) => k, value: (k) => map[k]);
filteredMap.forEach((k, v) => print('key: $k, value: $v'));
}
I use dartx and it's filter method
var myMap = {
"a": [1, 2, 3],
"b": [4, 5, 6],
"c": [7, 8, 9],
};
var result = myMap.filter((entry) => entry.key != "a");
You can just create an extension function and then use it anywhere in your code.
Put this in any file (I called mine MapUtils.dart)
extension MapUtils<K, V> on Map<K, V> {
Map<K, V> where(bool Function(K, V) condition) {
Map<K, V> result = {};
this.entries.forEach((element) {
if (condition(element.key, element.value)) {
result[element.key] = element.value;
}
});
return result;
}
}
and then use it like so:
Map<String, int> peopleHeight = {"Bob":170, "Alice":130};
Map<String, int> shortPeople = peopleHeight.where((name, height) => height < 140);

Dart List min/max value

How do you get the min and max values of a List in Dart.
[1, 2, 3, 4, 5].min //returns 1
[1, 2, 3, 4, 5].max //returns 5
I'm sure I could a) write a short function or b) copy then sort the list and select the last value,
but I'm looking to see if there is a more native solution if there is any.
Assuming the list is not empty you can use Iterable.reduce :
import 'dart:math';
main(){
print([1,2,8,6].reduce(max)); // 8
print([1,2,8,6].reduce(min)); // 1
}
If you don't want to import dart: math and still wants to use reduce:
main() {
List list = [2,8,1,6]; // List should not be empty.
print(list.reduce((curr, next) => curr > next? curr: next)); // 8 --> Max
print(list.reduce((curr, next) => curr < next? curr: next)); // 1 --> Min
}
You can now achieve this with an extension as of Dart 2.6:
import 'dart:math';
void main() {
[1, 2, 3, 4, 5].min; // returns 1
[1, 2, 3, 4, 5].max; // returns 5
}
extension FancyIterable on Iterable<int> {
int get max => reduce(math.max);
int get min => reduce(math.min);
}
An example to get Min/Max value using reduce based on condition for a list of Map objects
Map studentA = {
'Name': 'John',
'Marks': 85
};
Map studentB = {
'Name': 'Peter',
'Marks': 70
};
List<Map> students = [studentA, studentB];
// Get student having maximum mark from the list
Map studentWithMaxMarks = students.reduce((a, b) {
if (a["Marks"] > b["Marks"])
return a;
else
return b;
});
// Get student having minimum mark from the list (one liner)
Map studentWithMinMarks = students.reduce((a, b) => a["Marks"] < b["Marks"] ? a : b);
Another example to get Min/Max value using reduce based on condition for a list of class objects
class Student {
final String Name;
final int Marks;
Student(this.Name, this.Marks);
}
final studentA = Student('John', 85);
final studentB = Student('Peter', 70);
List<Student> students = [studentA, studentB];
// Get student having minimum marks from the list
Student studentWithMinMarks = students.reduce((a, b) => a.Marks < b.Marks ? a : b);
If your list is empty, reduce will throw an error.
You can use fold instead of reduce.
// nan compare to any number will return false
final initialValue = number.nan;
// max
values.fold(initialValue, (previousValue, element) => element.value > previousValue ? element.value : previousValue);
// min
values.fold(initialValue, (previousValue, element) => element.value < previousValue ? element.value : previousValue);
It can also use to calculate sum.
final initialValue = 0;
values.fold(initialValue, (previousValue, element) => element.value + previousValue);
Although fold is not cleaner than reduce for getting min/max, it is still a powerful method to do more flexible actions.
For empty lists: This will return 0 if list is empty, the max value otherwise.
List<int> x = [ ];
print(x.isEmpty ? 0 : x.reduce(max)); //prints 0
List<int> x = [1,32,5];
print(x.isEmpty ? 0 : x.reduce(max)); //prints 32
int minF() {
final mass = [1, 2, 0, 3, 5];
mass.sort();
return mass[0];
}
void main() {
firstNonConsecutive([1,2,3,4,6,7,8]);
}
int? firstNonConsecutive(List<int> arr) {
var max = arr.reduce((curr, next) => curr > next? curr: next);
print(max); // 8 --> Max
var min = arr.reduce((curr, next) => curr < next? curr: next);
print(min); // 1 --> Min
return null;
}
If you need a more sophisticated min/max, such as finding an object with a min/max of a field, or use of a comparison predicate, use minBy() and maxBy() from the collection package:
import 'package:collection/collection.dart';
class Person {
final String name;
final int age;
Person(this.name, this.age);
#override
String toString() => '$name (age $age)';
}
main() {
final alice = Person('Alice', 30);
final bob = Person('Bob', 40);
final chris = Person('Chris', 25);
final dan = Person('Dan', 35);
final people = [alice, bob, chris, dan];
print('Youngest is ${minBy(people, (e) => e.age)}');
print('Oldest is ${maxBy(people, (e) => e.age)}');
print('First alphabetically is ${minBy(people, (e) => e.name)}');
print('Last alphabetically is ${maxBy(people, (e) => e.name)}');
print('Largest name length times age is ${maxBy(people, (e) => e, compare: (a, b) => (a.name.length * a.age).compareTo(b.name.length * b.age))}');
}
Output:
Youngest is Chris (age 25)
Oldest is Bob (age 40)
First alphabetically is Alice (age 30)
Last alphabetically is Dan (age 35)
Largest name length times age is Alice (age 30)```

Resources