How to select random key (element) from Map?
I can do it using map.keys.toList(), as in the code below, but I wonder if there is more direct way?
import "dart:math";
void main() {
var map = {'a' :1, 'b':2, 'c':3};
final _random = new Random();
var keys = map.keys.toList();
var element = keys[_random.nextInt(keys.length)];
var r = map[element];
print(r);
}
There is no simple way to pick a "random" key from a map.
I assume that "random" here means to pick it uniformly at random among the keys of the map.
For that, you need to pick a random number in the range 0..map.length - 1. Then you need to get the corresponding key. Since Map.key is an iterable, you can't assume that you can do constant-time lookup in it, but you can use elementAt to get a specific iterable item without creating a new list.
So, basically:
randomKey(Map map) =>
map.keys.elementAt(new Random().nextInt(map.length));
(like you do it, but without the toList).
If you need more than one key, you are probably better off converting the keys to a list once, and then do lookups in the list in constant time. Example:
Iterable randomKeys(Map map) sync* {
var keys = map.keys.toList();
var rnd = new Random();
while (keys.length > 0) {
var index = rnd.nextInt(keys.length);
var key = keys[index];
keys[index] = keys.last;
keys.length--;
yield key;
}
}
On top of getting better performance, taking a copy of the keys also avoids concurrent modification errors.
If you are selecting multiple random values from the list and you want to make sure you never select an entry more than once, you can take the keys or values as a list, shuffle it then iterate through it.
This is not very efficient if only a small fraction of the entries in the map are to be selected.
void main() {
var map = { 'a' :1, 'b':2, 'c':3 };
// Keys
var keys = map.keys.toList()..shuffle();
for(var k in keys) {
print('$k, ${map[k]}');
}
// Values
var values = map.values.toList()..shuffle();
for(var v in values) {
print(v);
}
}
https://dartpad.dartlang.org/e49012d93f7451af1662ad113f0aab95
I guess this is not what you're looking for, but actually it's a line shorter ;-)
void main() {
var map = {'a' :1, 'b':2, 'c':3};
final _random = new Random();
var values = map.values.toList();
var element = values[_random.nextInt(values.length)];
print(element);
}
DartPad example
You can use the dart_random_choice package to help you. While Map itself is not an iterable, you can use the Map.keys method to get an iterable and do the following:
import 'package:dart_random_choice/dart_random_choice.dart';
void main() {
var map = { 'a': 1, 'b': 2, 'c':3 };
var r = map[randomChoice(map.keys)];
print(r);
}
Related
I have a map called oldMap with generics Map<String, List<int>>. I need to replace all the values in the oldMap. I have newValues with is off type List<List<int>> for each value. The keys will be the same. But need a way to iterate through newValues and add to the keys making a new map.
eg
oldMap = {'2': [14],'4': [11],'6': [2]}
newValues = [[11], [12], [19] ]// These values need to be in new map
newMap = {'2': [11],'4': [12],'6': [19]}
If you just want to replace the values in the existing key order, you can do:
var newMap = Map.fromIterables(oldMap.keys, newValues);
If you need some other key order, you need to figure out which, and then do something like:
var newMap = Map.fromIterables(sortKeysSomehow(oldMap.keys), newValues);
If you want to update the values in place, you can do:
var i = 0;
for (var key in map.keys) map[key] = newValues[i++];
or
var i = 0;
map.updateAll((k,v) => newValues[i++]);
I'm a beginner in dart.
void main() {
var abf = '+37.4054-122.0999/';
var abf2;
abf2 = abf.replaceAll("+"," ");
var abf1 = abf2.split(RegExp('(?=[+-])'));
print (abf1[0]);
print (abf1[1]);
}
The above code splits abf into two values for me
I want to remove the ending '/'. I tried many split methods using other variables but it's not removing the '/' even though its removing the '+'.
It's not really clear what you're trying to do with the split.
But if you're looking the remove the / this should work:
String number = '+37.4054-122.0999/';
number = number.replaceAll("/"," ");
You can create substring from this while you like to remove last element.
String abf = '+37.4054-122.0999/';
final result = abf.substring(0, abf.length - 1);
print(result);
Dart's List class has a built-in removeLast method. Maybe you can try to split the string and then removing the last element:
String str = "str";
String newStr = str.split(''). removeLast().join('');
I wrote this code to convert dynamic list to Word list but linter says:
Omit type annotations for local variables. on 2nd line.
However if I omit type annotations, I get an error A value of type 'List<dynamic>' can't be returned from method 'convert' because it has a return type of 'List<Word>'.
It there any smarter way to convert?
static List<Word> convert(List<dynamic> words) {
final List<Word> wordsList = [];
words.forEach((v) {
final map = Map<String, dynamic>.from(v as Map<dynamic, dynamic>);
wordsList.add(Word.fromMap(map));
});
return wordsList;
}
Word.fromMap is:
Word.fromMap(Map<String, dynamic> map)
: text = map['text'] as String,
count = map['count'] as int;
To avoid the warning, and put the type on the right-hand side as the lint wants, just write:
final wordsList = <Word>[];
I assume words is JSON data, so the maps are already Map<String, dynamic>. Then you can also do everything in one line:
static List<Word> convert(List<dynamic> words) =>
[for (var v in words) Word.fromMap(v)];
Use the cast() method like this:
class Word {
final String text;
final int count;
static List<Word> convert(List<dynamic> words) {
final List<Word> wordsList = [];
words.cast<Map<dynamic, dynamic>>().forEach((v) { // <-- look here
final map = Map<String, dynamic>.from(v);
wordsList.add(Word.fromMap(map));
});
return wordsList;
}
Word.fromMap(Map<String, dynamic> map)
: text = map['text'] as String,
count = map['count'] as int;
}
It will ensure the casting are done on each element. Make sure the type are correct since it will else result in a type-cast error.
I'm working with a framework and have to change some things for my project.
The original is
var tree:FBQuadTree? = nil
...
if tree == nil {
tree = FBQuadTree()
}
I need always a custom number of trees. So my consideration was that I use an array for this.
var tree:[FBQuadTree?] = []
...
if tree[number] == nil {
tree[number] = FBQuadTree()
}
But I don't know how to fill my new tree array. Sure I could do smth. like this:
let element:FBQuadTree?
layersTree.append(element)
Okay, but my problem is that number of elements isn't static. So sometimes there is just one element, sometimes a lot more.
Is that possible what I want to do?
the whole thing:
var array:[FBQuadTree] = []
//fill
for i in 0..<MyDynamicSize {
array.append(//how to fill?)
}
//give new values
for i in 0..<MyDynamicSize {
array[i] = FBQuadTree()
}
If you're just trying to get an Array of a myDynamicSize* number of FBQuadTree instances, you can use init(repeating:count:).
var array = [FBQuadTree](count: myDynamicSize, repeatedValue: FBQuadTree())
Sidenote: by convention, types start with capitals, whereas variable names start with lowercase letters. So myDynamicSize, rather than MyDynamicSize
please try this
var array:[FBQuadTree?] = []
//fill
for i in 0..<MyDynamicSize {
array.append(nil)
}
Given a Map, assignment, what is the fastest way to check if it contains any duplicate values in Dart? I am currently using a Set formed from the Map's values and checking its length against the original Map, which works of course, but I'm wondering if there's an especially performant alternative.
Set d = new Set.from(assignment.values);
if (d.length < assignment.length) {
return false; // indicates has duplicates in this context
}
EDIT:
Tried #mezoni's solution modified for my program, but it actually ran a bit slower than my original version. It probably has more to do with constant times than anything else.
List values = new List.from(assignment.values);
Set set = new Set();
for (var i = 0; i < assignment.length; i++) {
if (!set.add(values[i])) {
return false;
}
}
Complexity wise you won't be able to get anything faster. Creating the Set and filling it with the values of the Map is linear in the number of elements. Clearly you have to run through all the values, so you can't do any better than that.
Maybe you could find a solution with a smaller constant factor, but that's not clear. In particular for larger sets I think the Set solution is pretty efficient.
This is more of a algorithms question than a Dart question. In any case, you have to check every value against the others, giving n-1 + n-2 + ... + n-(n-1) checks, or n^2/2. Programmatically, it's easy to create a set, but you could also generate an array, sort the array, and then iterate once to check for duplicates. That finishes in O(n log n).
Fastets way (if you realy need better performance):
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var length = values.length;
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var i = 0; i < length; i++) {
statistics++;
if(!set.add(values[i])) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}
Output:
Duplicate: true
Performed in 4 iteration(s) from 8 possible
P.S.
The first example can be recommended to use with List values.
But because Map.values not a List but Iterable then it would be more efficient do not convert them to List but use as is.
Here is modified sample for use with Iterable objects.
It will be work faster because in this algorithm not required convert all values to the List object because it not want using of all elements without exception.
Instead it wants use as less as possible access operation on original source. If the source supports lazy operation of the access to values (as Iterable) this will be even better.
void main() {
// Values from map
var values = [1,2,3,2,1,3,2,1];
var assignment = {};
var length = values.length;
var key = 0;
for(var value in values) {
assignment[key++] = value;
}
var set = new Set();
var duplicate = false;
// Only for statistics purpose
var statistics = 0;
for(var value in assignment.values) {
statistics++;
if(!set.add(value)) {
duplicate = true;
break;
}
}
print("Duplicate: $duplicate");
print("Performed in ${statistics} iteration(s) from $length possible");
}