Dart - Flatten a list while removing duplicate values - dart

So, I have a list of lists, like this
var a = [['a', 'b', 'c'], ['1', '2', 'b'], [3, 'd', true]];
I want to output a flattened list with duplicate values removed -
var b = ['a', 'b', 'c', '1', '2', 3, 'd', true];
While, I know I can use map/reduce, just trying to understand what is the most dart like way of doing this.
More info about input - I'm just trying to understand iterables and sets better in Dart (coming from Python world). Dart Trickeristry welcome!

You want to flatten the list of lists and remove duplicates.
The way to remove duplicates is to use a Set with an efficient lookup (or something equivalent, but Set is the simplest de-duplicating functionality).
Any other approach is likely going to be quadratic because it checks each element against each other element.
For that, I'd do:
var result = [...{for (var list in lists) ...list}];
This flattens the lists into a set, then converts that set to a list again.
The one other alternative would be flattening into a list, sorting the list, and then removing adjacent duplicates. That requires the elements to be Comparable, which the example given here isn't, or that you provide a comparison function. Let's assume the latter:
List<T> flatUnique(Iterable<Iterable<T>> elements, int compare(T a, T b)) {
var flat = [for (var element in elements) ...element];
if (flat.isEmpty) return flat;
flat.sort(compare);
var current = flat[0];
var j = 1;
for (var i = 1; i < flat.length; i++) {
var next = flat[i];
if (current != next) {
current = flat[j++] = next;
}
}
flat.length = j;
return flat;
}
That's more complicated because there is not a general "remove duplicates from sorted list" operation in the libraries ... because people just use a set.
It also changes the order of elements other than by removing duplicates.

in dart 2.3,
you can actually use spread operator like this:
var a = [['a', 'b', 'c'], ['1', '2', 'b'], [3, 'd', true]];
var b = [...a[0], ...a[1], ...a[2]];
// print ['a', 'b', 'c', '1', '2', 3, 'd', true];
and do your code afterwards

You can combine a Set with the expand method.
var a = [['a', 'b', 'c'], ['1', '2', 'b'], [3, 'd', true]];
var b = Set.from(a.expand((x) => x)).toList();
// [a, b, c, 1, 2, 3, d, true]

Related

How cascading modifies original object?

var intList = [3, 2, 1];
var sorted = intList..toList()..sort(); // [1, 2, 3]
var sorted2 = intList..toList().sort(); // [3, 2, 1]
Why my original list is also being modified in first sort and which list is being sorted in second sort?
NOTE: I'm not looking for the correct way to do it which is this:
var sorted = intList.toList()..sort(); // [1, 2, 3]
x..y evalutes to x. Cascade chains are evaluated left-to-right, so x..y..z is the same as (x..y)..z. Your first example therefore makes calls to toList() and to sort() on the original object.
Member access (.) has higher precedence than the cascade operator (..). Your second example calls sort() on the copy returned by toList(), not on the original object.

Convert nested list (2d list) to one list of elements using built-in methods like map() in Dart

How can I convert 2d list to 1d list with all the elements in dart?
I have this 2d List:
List<List<int>> list2d = [[1, 2], [3, 4]];
What I want is:
List<int> list1d = [1, 2, 3, 4];
By converting the first one (2d) to the second (1d) but without writing any (for/while) loops code, if there any built-in methods like map()/where()/cast()/...etc.
Any ideas?
As other have pointed out, expand does what you want
var list2d = [[1, 2], [3, 4]];
var list1d = list2d.expand((x) => x).toList();
You can also, and perhaps preferably, use a list literal:
var list1d = [for (var list in list2d) ...list];
In general, iterable.expand((x) => e).toList() is equivalent to [for (var x in iterable) ...e].
Simply by using the reduce function like this:
List<int> list1d = list2d.reduce((value, element) {
value.addAll(element);
return value;
});
Definition:
List<T> reduce(List<T> Function(List<T>, List<T>) combine);
You can just use the .expand method:
List<int> list1d = list2d.expand((e) => e).toList();

dart how to assign list into a new list variable

I am trying to extend a list just by using add method like this
List<String> mylists = ['a', 'b', 'c'];
var d = mylists.add('d');
print(d);
It gives error
This expression has type 'void' and can't be used.
print(d);
Why i cannot save the list in a new variable? Thank you
mylists.add('d') will add the argument to the original list.
If you want to create a new list you have several possibilities:
List<String> mylists = ['a', 'b', 'c'];
// with the constructor
var l1 = List.from(mylists);
l1.add('d');
// with .toList()
var l2 = mylists.toList();
l2.add('d');
// with cascade as one liner
var l3 = List.from(mylists)..add('d');
var l4 = mylists.toList()..add('d');
// in a upcoming version of dart with spread (not yet available)
var l5 = [...myList, 'd'];
Refering Dart docs: https://api.dartlang.org/stable/2.2.0/dart-core/List-class.html
The add method of List class has return type of void.
So you were unable to assign var d.
To save list in new variable use:
List<String> mylists = ['a', 'b', 'c'];
mylists.add('d');
var d = mylists;
print(d);
First add the new String i.e. 'd'
And then assign it to new variable

Rails Query Active Record By Pairs OR'ed

I have an API that looks like...
get(not_unique_id:, pairs: [])
...where pairs can look like [{ foo: 1, bar: 'a' }, { foo: 2, bar: 'b' }] and I want to write a query that will find all entries for the given non unique id, filtered by the pairs, i.e. ...
Thing.where(not_unique_id: not_unique_id)
# need help here -> .where("(foo = #{pairs.first[:foo]} AND bar = #{pairs.first[:bar]}) OR (foo = #{pairs.second[:foo]} AND bar = #{pairs.second[:bar]}) OR ...")
.map # or whatever else I want to do
I need a way to perform this foo AND bar comparison for each entry in my input pairs, OR'ed together to return all results that are have the not unique ID AND one of the pairs' values.
You can use or to chain together multiple where clauses with ORs:
Record.where(a: 'b').or(Record.where(c: 'd')).to_sql
# => select * from records where (a = 'b' OR c = 'd')
Using this and your array of conditions, you can provide a starting "seed" of Record.all to a reduce call and chain multiple successive conditions by oring them onto the scope:
conditions = [{ foo: 1, bar: 'a' }, { foo: 2, bar: 'b' }]
records = conditions.inject(Record.all) do |scope, condition|
scope.or(Record.where(condition))
end

How to sort map value?

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) {
...
}

Resources