Consider: ['A', 'B', 'C']
I'd like to do
list.move("B", -1);
list.move("A", 1);
resulting in ["B", "C", "A"].
Is there some sort of one liner to move items in a listin Dart? Obviously could do this manually in a few lines just curious if there is some easier way.
There is indeed no built-in way to do this.
Since the list keeps the same length, the most efficient approach would move the elements in-place rather than remove an element and then insert it again. The latter requires moving all later elements as well, where doing it in-place only requires moving the elements between from and to.
Example:
extension MoveElement<T> on List<T> {
void move(T element, int offset) {
var from = indexOf(element);
if (from < 0) return; // Or throw, whatever you want.
var to = from + offset;
// Check to position is valid. Or cap it at 0/length - 1.
RangeError.checkValidIndex(to, this, "target position", length);
element = this[from];
if (from < to) {
this.setRange(from, to, this, from + 1);
} else {
this.setRange(to + 1, from + 1, this, to);
}
this[to] = element;
}
}
I would probably prefer a more general "move" function like:
extension MoveElement<T> on List<T> {
void move(int from, int to) {
RangeError.checkValidIndex(from, this, "from", length);
RangeError.checkValidIndex(to, this, "to", length);
var element = this[from];
if (from < to) {
this.setRange(from, to, this, from + 1);
} else {
this.setRange(to + 1, from + 1, this, to);
}
this[to] = element;
}
}
and then build the "find element, then move it relative to where it was" on top of that.
linear? I don't think there is any. In any case, a sample code here:
extension on List<String> {
void move(String element, int shift) {
if (contains(element)) {
final curPos = indexOf(element);
final newPos = curPos + shift;
if (newPos >= 0 && newPos < length) {
removeAt(curPos);
insert(newPos, element);
}
}
}
}
void main(List<String> args) {
var list = ['A', 'B', 'C', 'D', 'E', 'F'];
print(list);
list.move('B', 2);
print(list);
list.move('B', -3);
print(list);
}
Result:
[A, B, C, D, E, F]
[A, C, D, B, E, F]
[B, A, C, D, E, F]
Related
I am using fold on an array which hasn't been assign to a variable and want to check whether the element is the last value. With a conventional for loop I can do this:
List<int> ints = [1, 2, 3];
int sum = 0;
for (int num in ints]) {
if (num != ints.last) {
sum = sum + num;
}
}
print(sum);
Is it possible to do this with fold instead?
int foldSum = [1, 2, 3].fold(0, (int prev, element) => prev + element);
print(foldSum);
I can't find any way of check when fold is at the last value. Note: this is a simplified example of my problem and the reason the list isn't assigned to a variable (allowing me to use .last) is because it is the result of a call to .map().
For completeness, below is the actual code (which won't obviously won't be runnable in isolation but will help illustrate my problem) I am trying to convert to use .map and .fold:
String get fieldsToSqlInsert {
String val = "";
for (Column column in columns) {
if (data.containsKey(column.name)) {
val = '$val "${data[column.name]}"';
} else {
val = "$val NULL";
}
if (column != columns.last) {
val = "$val,";
}
}
return val;
}
But it doesn't work because I don't know how to check when fold is at the final element:
String get fieldsToSqlInsert => columns
.map((column) =>
data.containsKey(column.name) ? data[column.name] : "NULL")
.fold("", (val, column) => column != columns.last ? "$val," : val);
If you simply want to exclude the last element from further calculation, you can just use take to do so:
String get fieldsToSqlInsert => columns.take(columns.length - 1)...
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 a better/faster way in Dart to rotate a list?
List<Object> rotate(List<Object> l, int i) {
i = i % l.length;
List<Object> x = l.sublist(i);
x.addAll(l.sublist(0, i));
return x;
}
Could be simplified a bit
List<Object> rotate(List<Object> list, int v) {
if(list == null || list.isEmpty) return list;
var i = v % list.length;
return list.sublist(i)..addAll(list.sublist(0, i));
}
If you want to shift instead of rotate you can simply use the removeAt function:
List<int> list = [ 1, 2, 3 ];
int firstElement = list.removeAt(0);
print(list); // [ 2, 3 ]
print(firstElement); // 1
From the docs:
Removes the object at position [index] from this list.
This method reduces the length of this by one and moves all later objects down by one position.
Returns the removed value.
The [index] must be in the range 0 ≤ index < length. The list must be growable.
Here are some more useful JS shims.
Is there a better idiom for auto-initializing Map values to 0 than the following? In the following code there is an asymmetry between the approach to adding a value to a target of type List versus int.
main() {
addToList(Map m, v) =>
m..putIfAbsent('foo', () => []).add(v);
///////////////////////////////////////////////////////////
// Not allowed (expression is not assignable)
// addToScalar(Map m, v) =>
// m..putIfAbsent('foo', () => 0) += 3;
addToScalar1(Map m, v) {
m.putIfAbsent('foo', () => 0);
m['foo'] += v;
return m;
}
addToScalar2(Map m, v) {
if(m.containsKey('foo')) {
m['foo'] += v;
} else {
m['foo'] = v;
}
return m;
}
print(addToList({}, 3));
print(addToScalar1({}, 3));
print(addToScalar2({}, 3));
}
Conceptually addToList and addToScalar do similar things. But the analog for the int stored as a value type might be:
m.putIfAbsent('foo', () => 0) += someValue
which will not work since what is returned from putIfAbsent is not assignable. So with both the working approaches used in the scalar case the lookup in the map for key 'foo' is being done twice. Can this be avoided with the Map API?
No, you cannot currently avoid two lookups in order to modify a map value.
We have considered, but never decided on, a way to achieve that (e.g., an "update" method).
The Two shortest/most efficient solution to your problem are:
int _returnZero() => 0; // Toplevel or static function.
...
map[key] = map.putIfAbsent(key, _returnZero) + v;
...
and:
int value = map[key];
map[key] = (value == null) ? v : value + v;
List data = [1, 2, 3];
data.forEach((value) {
if (value == 2) {
// how to stop?
}
print(value);
});
I tried return false; which works in jQuery, but it does not work in Dart.
Is there a way to do it?
You can also use a for/in, which implicitly uses the iterator aptly demonstrated in the other answer:
List data = [1,2,3];
for(final i in data){
print('$i');
if (i == 2){
break;
}
}
It is also possible to implement your example using forEach() and takeWhile().
var data = [1, 2, 3];
data.takeWhile((val) => val != 2).forEach(print);
Breaking a List
List<int> example = [ 1, 2, 3 ];
for (int value in example) {
if (value == 2) {
break;
}
}
Breaking a Map
If you're dealing with a Map you can't simply get an iterator from the given map, but you can still use a for by applying it to either the values or the keys. Since you sometimes might need the combination of both keys and values, here's an example:
Map<String, int> example = { 'A': 1, 'B': 2, 'C': 3 };
for (String key in example.keys) {
if (example[key] == 2 && key == 'B') {
break;
}
}
Note that a Map doesn't necessarily have they keys as [ 'A', 'B', 'C' ] use a LinkedHashMap if you want that. If you just want the values, just do example.values instead of example.keys.
Alternatively if you're only searching for an element, you can simplify everything to:
List<int> example = [ 1, 2, 3 ];
int matched = example.firstMatching((e) => e == 2, orElse: () => null);
The callback that forEach takes returns void so there is no mechanism to stop iteration.
In this case you should be using iterators:
void listIteration() {
List data = [1,2,3];
Iterator i = data.iterator;
while (i.moveNext()) {
var e = i.current;
print('$e');
if (e == 2) {
break;
}
}
}
Dart does not support non-local returns, so returning from a callback won't break the loop.
The reason it works in jQuery is that each() checks the value returned by the callback.
Dart forEach callback returns void.
http://docs.jquery.com/Core/each
based on Greg Lowe post, I used where for my project and also it works.
var data = [1, 2, 3];
data.where((val) => val != 2).forEach(print);
Using Multiple Loop
Break Outer Loop
OUTER: for (var i = 0; i < m.length; i++) {
for (var j = 0; j < m[i].length; j++) {
if (m[i][j] < 0) {
print("Negative value found at $i,$j: ${m[i][j]}");
break OUTER;
}
}
}
Continue Outer Loop
outer: for (var v in a) {
for (var w in b) {
if (w == v) continue outer;
}
print(v);
}
Here is a full sample by for-in loop, that close to forEach style.
void main(){
var myList = [12, 18, 24, 63, 84,99];
myList.forEach((element) {
print(element);
if (element ==24); //break; // does not work
});
for(var element in myList) {
print(element);
if (element==24) break;
}
}
Somebody suggest where() but it is not a general replacement for forEach() with break capability
(where is however a correct replacement for the use case showed in the example of the question. I, on the other hand, focus on the question in the title)
The functionality of foreach() but with an equivalent of break, is given by any(): to continue the loop you return false, to stop you return true; the result of any() can be ignored. I think it is more similar to each() in jquery (but in dart to stop you return true).
To have a loop with the index, but also the possibility in case of break the loop, I use the following extension:
extension IterableUtils<E> on Iterable<E> {
/**
Similar to Iterable.forEach() but:
- with an index argument
- with the optional capacity to break the loop, returning false
Note: as for the return clause, you can omit it, as with forEach()
*/
void forEachIndexed(Function(E element, int index) f) {
int index = 0;
for (E element in this) {
if (f(element, index) == false) break;
index++;
}
}
}
Example:
void main() {
List list = ["a", "b", "c"];
list.forEachIndexed((element, index) {
print("$index: $element");
//Optional:
if (element == "b") return false; //break
});
}
You CAN empty return from a forEach to break the loop;
List<int> data = [1, 2, 3];
int _valueToBePrinted;
data.forEach((value) {
if (value == 2) {
_valueToBePrinted = value;
return;
}
});
// you can return something here to
// return _valueToBePrinted;
print(value);
anyway you shouldn't...
the catch is, you can't return anything in the entire forEach loop
//This don't work
data.forEach((value) {
if (value == 2) {
_valueToBePrinted = value;
return;
}
if (value == 1) {
return value;
}
});