Assume I want to concatenate N Uint8Lists into a single one.
The naive approach is to simple copy all elements into a new list. However, that seems rather memory in efficient. Instead, I want to create a single Uint8List "view" which simply indexes into the appropriate underlying list instead of copying all its content.
In C++ I'd usually just overwrite operator[] but I am not quite certain how to do this with Uint8Lists in Dart.
In C++, you can make a View class that overrides operator[]. In Dart, you could do the same thing:
class View<T> {
View(this._lists);
List<List<T>> _lists;
T operator [](int index) {
for (var list in _lists) {
if (index < list.length) {
return list[index];
}
index -= list.length;
}
throw RangeError('...');
}
}
You could stop there, but doing just that usually wouldn't be enough in either language. In C++, you'd also want to provide begin() and end() methods for range-based for loops to work. Similarly, in Dart, you'd want to provide the Iterable interface so that for-in would work.
Luckily package:collection (note that this is separate from dart:collection) provides a CombinedListView class that does that work for you. For example:
import 'dart:typed_data';
import 'package:collection/collection.dart';
void main() {
var list1 = Uint8List.fromList([1, 2, 3]);
var list2 = Uint8List.fromList([4, 5, 6]);
var list3 = Uint8List.fromList([7, 8, 9]);
var view = CombinedListView<int>([list1, list2, list3]);
for (var i in view) {
print(i);
}
}
Related
I need to loop through list items and I need index, not value.
I know three options:
Classic:
for (final i = 0; i < list.length; i++) {
With asMap:
for (final i in list.asMap().keys)
With Iterable:
for (final i in Iterable.generate(list.length)) {
Non-classic options look to be easier to read, write and less error prone.
How about performance? It seems both options produce iterable, so they should not create performance overhead. Is it correct assessment or I am missing something?
Micro benchmark here (usual caveats apply) https://gist.github.com/jakemac53/16c782ed92f6bbceb98ad83cd257c760.
If your code is perf sensitive, use the "classic" for loop.
While existing alternatives for the classic way are not great from performance perspective, there is work in progress to introduce a better way to loop lists: https://github.com/dart-lang/collection/pull/259#discussion_r1090563595
For now you the extension will enable nice looping:
extension Indexes<T> on List<T> {
Iterable<int> get indexes sync* {
for (var i = 0; i < length; i++) yield i;
}
}
Then we can write:
for (final i in list.indexes)
Using package:collection, you can get a .mapIndexed on an Iterable to give you both the item and the index of the item.
final result = someList.mapIndexed((n, e) => "item $n is $e");
I have a list of elements and I need to get a list containing the first element followed by every nth element afterwards. For example: given n = 3 and the list [banana, cherry, apple, pear, kiwi], I need to get the list [banana, pear]. I need this regardless of specific content, since the list depends on user input.
How do I do this using Dart?
You may access list in dart by providing an index like for example:
List<String> fruits = ["banana","cherry","apple","pear","kiwi"];
print(fruits[0]); // Will print to the console "banana";
On your case, you are trying to access index 0 and index 3 which is "banana" and "pear".
You may create a function that accepts an index like:
String getFruit(int index, List<String> fruits) => fruits[index];
print(getFruit[0]); // Will print "banana";
or if you need to actually get the specific ranges you may use:
List<String> fruits =["banana","cherry","apple","pear","kiwi"].getRange(0,4);
// Will give you "banana","cherry","apple","pear
You may check : https://api.dart.dev/be/180791/dart-core/List-class.html for more information.
Edited answer based off the comment:
List<String> getElements(List userInput, nIndex){
List elements = [];
for(int x = 0; x<userInput.length;x++){
if(x % nIndex == 0){
elements.add(userInput[x]);
}
}
return elements;
}
List fruits = ["banana","cherry","apple","pear","kiwi"];
print(getElements(fruits,2));
or you may try to look and use List.retainWhere() depending on your use case.
Dart has a great set of collection operators that make this type of problem pretty straightforward to solve. For example, we could do something like:
extension X<T> on List<T> {
List<T> everyNth(int n) => [for (var i = 0; i < this.length; i += n) this[i]];
}
main() {
final fruit = ["banana", "cherry", "apple", "pear", "kiwi"];
print(fruit.everyNth(3));
}
Output:
[banana, pear]
You can use this extension method, which will work on lists of any type:
extension GetEveryN<T> on List<T> {
List<T> elementsEveryN(int n) {
List<T> result = [];
for(int index = 0; index < length; index +=1) {
if(index % n == 0) {
result.add(this[index]);
}
}
return result;
}
}
Trying it in an example:
List<String> list = ["banana", "cherry","apple", "pear","kiwi"];
print(list.elementsEveryN(2)); // [banana, pear]
I tried writing a simple generic method that would iteratively copy a nested List, for example a List<List<int>>. But unfortunately, the recursive call seems to always return List<dynamic>, so I get the following error
The argument type List<dynamic> can't be assigned to the parameter type T
List<T> listDeepCopy<T>(List<T> list){
List<T> newList = List<T>();
list.forEach((value) {
if( value is List ){
newList.add(listDeepCopy(value)); // <-- listDeepCopy() always returns List<dynamic>
}
else{
newList.add(value);
}
});
return newList;
}
So if I call
List<List<int>> list = [[1,2],[3,4]];
List<List<int>> copy = listDeepCopy(list);
T is List<int>
value is T - i.e. List<int>
listDeepCopy(value) should equal listDeepCopy<List<int>>, which would return a List<int>, which should be possible to add to newList, which is a List<List<int>>
Where am I going wrong here, and how can I make something like this work?
I probably would implement it as:
List<T> listDeepCopy<T>(List<T> list) {
var copy = list.toList();
for (var i = 0; i < copy.length; i += 1) {
var element = copy[i];
if (element is List) {
copy[i] = listDeepCopy(element) as T;
}
}
return copy;
}
void main() {
List<List<int>> list = [
[1, 2],
[3, 4]
];
List<List<int>> copy = listDeepCopy(list);
list[0][0] = 99;
print(copy); // Prints: [[1, 2], [3, 4]]
}
A problem with your approach is that Dart cannot properly infer the generic type parameter for that recursive listDeepCopy(value) call. value is of type T that is known to be a List (which is shorthand for List<dynamic>), and I am not aware of a way to extract the static element type. (Maybe #lrn will see this and provide a better, more complete explanation.)
In such a case, it's better to rely on polymorphism by calling a method on the List that returns a copy of itself: .toList().
(As an example where this matters, consider a shallow copy scenario:
List<T> shallowCopy1<T>(List<T> list) => <T>[...list];
List<T> shallowCopy2<T>(List<T> list) => list.toList();
extension StaticType<T> on T {
Type get staticType => T;
}
void main() {
List<num> list = <int>[1, 2, 3];
var copy1 = shallowCopy1(list);
var copy2 = shallowCopy2(list);
print('original: staticType: ${list.staticType}, runtimeType: ${list.runtimeType}');
print('copy1: staticType: ${copy1.staticType}, runtimeType: ${copy1.runtimeType}');
print('copy2: staticType: ${copy2.staticType}, runtimeType: ${copy2.runtimeType}');
}
Although both copies preserve the static type of the original List, only copy2 preserves the object's actual (runtime) type. A proper copy depends on the runtime type of the object being copied, and the only robust way to do that is for the object to create a copy of itself.)
You can't do it the way you are trying to do it.
The problem is that deepClone<T> converts a List<dynamic> to a List<T> (which is fine) and then tries to convert elements that are themselves lists into typed list ... but you don't know the type.
In effect, when you check that value is List, you don't know what kind of list to convert it to.
There are two cases:
Either T is List<X> or Iterable<X> for some type X, but you have no way to get your hands on that X. Dart doesn't allow you to destructure types at runtime.
Or T is Object or another general supertype with no "list element" type inside it, and then you simply do not have any information about what List type to convert the nested list to. (That's actually the simplest case, because then you should simply not deepClone the list at all).
There is a way to figure out which case you are in (<T>[] is List<Iterable<Object?>>), but it won't help you in the former case, unless you want to do an exhaustive search of all the possible types that X might be.
What I'd do instead is to build a converter, instead of using a single function.
abstract class Cloner<T> {
const factory Cloner() = _ValueCloner<T>;
T clone(dynamic source);
Cloner<List<T>> get list => _ListCloner(this);
}
abstract class _BaseCloner<T> implements Cloner<T> {
const _BaseCloner();
Cloner<List<T>> get list => _ListCloner<T>(this);
}
class _ValueCloner<T> extends _BaseCloner<T> {
const _ValueCloner();
T clone(dynamic source) => source as T;
}
class _ListCloner<T> extends _BaseCloner<List<T>> {
final Cloner<T> _base;
_ListCloner(this._base);
List<T> clone(dynamic source) =>
<T>[for (var o in source as List<dynamic>) _base.clone(o)];
}
Then, if you actually know the type of the data, you can build your cloner as:
var typedList =
Cloner<int>().list.list.clone(
<dynamic>[<dynamic>[1, 2], <dynamic>[3, 4]]);
which yields a List<List<int>> with the value <List<int>>[<int>[1, 2], <int>[3, 4]].
In pubspec.yaml, I'm using english_words library to generate wordpairs:
dependencies:
flutter:
sdk: flutter
# Contains a few thousand of the most used English words
# plus some utility functions.
english_words: ^3.1.0
Now the WordPair Class is not a subtype of String and so I can't use the Iterable's lambdas or functions like cast or retype to 'cast' the 'WordPairs' to Strings.
So, I had to write the function called getWords().
See below the Dart file, Model.dart, that contains this implementation.
You'll see the old line commented out where it was returning in the getter the type Iterable.
Would there be a more efficient way to do this?
For example, I didn't want to involve a List Class in the conversion, but I can't find any other way to successfully do this.
Thanks.
---------------- Model.dart
import 'package:english_words/english_words.dart' show WordPair, generateWordPairs;
import 'dart:collection';
/// Model Class
///
class Model {
String get randomWordPair => new WordPair.random().asPascalCase;
// Iterable<WordPair> get wordPairs => generateWordPairs().take(10);
Iterable<String> get wordPairs => getWords();
Iterable<String> getWords(){
Iterable<WordPair> pairs = generateWordPairs().take(10);
ListWords<String> words = new ListWords();
for (var pair in pairs) {
words.add(pair.asString);
}
return words;
}
}
class ListWords<E> extends ListBase<E> {
final List<E> l = [];
set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
}
In Dart 2 you can use
iterable.cast<NewType>()
but it is prone to lead to inefficiency if the resulting list is accessed often, because it wraps the original iterable into a new one and has to forward every access.
Usually more efficient are
new List<NewType>.of(oldList)
or
new List.from<NewType.from(oldList)
I was not able to derive the difference between .of() and from() from the docs though (https://api.dartlang.org/dev/2.0.0-dev.50.0/dart-core/List/List.from.html, https://api.dartlang.org/dev/2.0.0-dev.50.0/dart-core/List/List.of.html)
At first glance, a loop that is collecting the result of an expression can generally be replaced with an appropriate .map method invocation on an Iterable. See if that will help.
I am trying to use a HashMap of Lists of strings in Vala, but it seems the object lifecycle is biting me. Here is my current code:
public class MyClass : CodeVisitor {
GLib.HashTable<string, GLib.List<string>> generic_classes = new GLib.HashTable<string, GLib.List<string>> (str_hash, str_equal);
public override void visit_data_type(DataType d) {
string c = ...
string s = ...
if (! this.generic_classes.contains(c)) {
this.generic_classes.insert(c, new GLib.List<string>());
}
unowned GLib.List<string> l = this.generic_classes.lookup(c);
bool is_dup = false;
foreach(unowned string ss in l) {
if (s == ss) {
is_dup = true;
}
}
if ( ! is_dup) {
l.append(s);
}
}
Note that I am adding a string value into the list associated with a string key. If the list doesn't exist, I create it.
Lets say I run the code with the same values of c and s three times. Based some printf debugging, it seems that only one list is created, yet each time it is empty. I'd expect the list of have size 0, then 1, and then 1. Am I doing something wrong when it comes to the Vala memory management/reference counting?
GLib.List is the problem here. Most operations on GLib.List and GLib.SList return a modified pointer, but the value in the hash table isn't modified. It's a bit hard to explain why that is a problem, and why GLib works that way, without diving down into the C. You have a few choices here.
Use one of the containers in libgee which support multiple values with the same key, like Gee.MultiMap. If you're working on something in the Vala compiler (which I'm guessing you are, as you're subclassing CodeVisitor), this isn't an option because the internal copy of gee Vala ships with doesn't include MultiMap.
Replace the GLib.List instances in the hash table. Unfortunately this is likely going to mean copying the whole list every time, and even then getting the memory management right would be a bit tricky, so I would avoid it if I were you.
Use something other than GLib.List. This is the way I would go if I were you.
Edit: I recently added GLib.GenericSet to Vala as an alternative binding for GHashTable, so the best solution now would be to use GLib.HashTable<string, GLib.GenericSet<string>>, assuming you're okay with depending on vala >= 0.26.
If I were you, I would use GLib.HashTable<string, GLib.HashTable<unowned string, string>>:
private static int main (string[] args) {
GLib.HashTable<string, GLib.HashTable<unowned string, string>> generic_classes =
new GLib.HashTable<string, GLib.HashTable<unowned string, string>> (GLib.str_hash, GLib.str_equal);
for (int i = 0 ; i < 3 ; i++) {
string c = "foo";
string s = i.to_string ();
unowned GLib.HashTable<unowned string, string>? inner_set = generic_classes[c];
stdout.printf ("Inserting <%s, %s>, ", c, s);
if (inner_set == null) {
var v = new GLib.HashTable<unowned string, string> (GLib.str_hash, GLib.str_equal);
inner_set = v;
generic_classes.insert ((owned) c, (owned) v);
}
inner_set.insert (s, (owned) s);
stdout.printf ("container now holds:\n");
generic_classes.foreach ((k, v) => {
stdout.printf ("\t%s:\n", k);
v.foreach ((ik, iv) => {
stdout.printf ("\t\t%s\n", iv);
});
});
}
return 0;
}
It may seem hackish to have a hash table with the key and value having the same value, but this is actually a common pattern in C as well, and specifically supported by GLib's hash table implementation.
Moral of the story: don't use GLib.List or GLib.SList unless you really know what you're doing, and even then it's generally best to avoid them. TBH we probably would have marked them as deprecated in Vala long ago if it weren't for the fact that they're very common when working with C APIs.
Vala's new can be a little weird when used as a parameter. I would recommend assigning the new list to a temporary, adding it to the list, then letting it go out of scope.
I would also recommend using libgee. It has better handling of generics than GLib.List and GLib.HashTable.