Is there a way to get a default value from a Map in dart (like Java):
static Map DEFAULT_MAPPING = Map.unmodifiable({
"k1": "value"
});
DEFAULT_MAPPING['k1'] //get 'value'
DEFAULT_MAPPING.getOrElse('non-present-key', 'default-value') //something like Java has
If your map does not contains null values, you can use if null operator:
var map = {
'a': 1,
'b': 2,
};
var cValue = map['c'] ?? 3;
Alternativelly you can define your own extension method:
extension DefaultMap<K,V> on Map<K,V> {
V getOrElse(K key, V defaultValue) {
if (this.containsKey(key)) {
return this[key];
} else {
return defaultValue;
}
}
}
var map = {
'a': 1,
'b': 2,
};
var cValue = map.getOrElse('c', 3);
Related
Anyone know if this is a Dart bug or is it my misunderstanding of how Dart coding works?
I am learning Dart to investigate feasibility of eventually using Flutter; however, while exploring the language, I found a weird behavior (maybe a bug). I tried repro'ing it by writing a similar pattern of code, but have yet to figure out what causes it. In the attached code, I wrote a quicksort class. In that class, it counts the number of times the "sort" method is recursed and saves the count in a class member called "recurseCount".
From the main() class, if I use the QuickSort class directly, I have no issue getting back the recurseCount member; however, if I call it from a different class (called "Tester"), I do not get the correct value for "recurseCount". Why would calling a class from a separate class cause members to not provide the correct values?
import 'package:test/test.dart';
import 'dart:math' as _math;
// ***********************
enum SortOrder { ascending, descending, unsorted }
class QuickSort {
List list = [];
SortOrder sortOrder = SortOrder.unsorted;
int recurseCount = 0;
QuickSort({this.list}) {
if (list != null && list.length > 1) {
list = sort(useRandomPivot: true);
}
}
List sort(
{List iList,
int leftIndex = 0,
int rightIndex,
bool useRandomPivot = true}) {
if (iList != null) list = iList;
if (list.isEmpty) return [];
rightIndex ??= list.length - 1;
if (rightIndex > list.length - 1) rightIndex = list.length - 1;
if (leftIndex < rightIndex) {
recurseCount++;
var partitionIndex =
_partition(leftIndex, rightIndex, useRandomPivot: useRandomPivot);
if (partitionIndex == -1) {
//already sorted List
if (sortOrder == SortOrder.ascending) {
return list;
} else {
//SortOrder.descending
list = list.reversed.toList(); // Time Complexity of O(n)
sortOrder = SortOrder.ascending;
return list;
}
} else {
sort(leftIndex: leftIndex, rightIndex: partitionIndex - 1);
sort(leftIndex: partitionIndex + 1, rightIndex: rightIndex);
}
} else {
sortOrder = SortOrder.ascending;
}
return list;
}
int _partition(int leftIndex, int rightIndex, {bool useRandomPivot = true}) {
// in case the array is already sorted from the start; only run through the partition'ing one time
// note: regardless of ascending or descending order
if (leftIndex == 0 && rightIndex >= list.length - 1) {
sortOrder = checkSorting(list);
if (sortOrder != SortOrder.unsorted) {
return -1;
}
if (useRandomPivot) {
var random = _math.Random();
var randomIndex = random.nextInt(rightIndex - leftIndex);
_swapElements(randomIndex, rightIndex);
}
}
int pivotVal = list[rightIndex]; //select the last item as the pivot
var headIndex = leftIndex - 1;
for (var scanIndex = leftIndex; scanIndex < rightIndex; scanIndex++) {
if (list[scanIndex] <= pivotVal) {
headIndex++;
_swapElements(headIndex, scanIndex);
}
}
var partitionIndex = headIndex + 1;
_swapElements(partitionIndex, rightIndex);
return partitionIndex;
}
void _swapElements(position1, position2) {
int tempVal = list[position1];
list[position1] = list[position2];
list[position2] = tempVal;
}
SortOrder checkSorting(List arr) {
var isAsc = true;
var isDesc = true;
for (var i = 0; i < arr.length - 1; i++) {
if (arr[i] < arr[i + 1]) isDesc = false;
if (arr[i] > arr[i + 1]) isAsc = false;
if (!isDesc && !isAsc) break; // not sorted Asc or Desc
}
if (isAsc) {
return SortOrder.ascending;
} else if (isDesc) return SortOrder.descending;
return SortOrder.unsorted;
}
}
// ***********************TESTS*********************************
num log2(num n) => _math.log(n) / _math.ln2;
List getRandomIntList({int min = 0, int max = 10000, int len}) {
var random = _math.Random();
var tempList = List(len);
for (var i = 0; i < len; i++) {
tempList[i] = random.nextInt(max - min);
}
return tempList;
}
class Tester {
static num _runControlTest(List list) {
var stopwatch = Stopwatch();
stopwatch.start();
list.sort();
stopwatch.stop();
return stopwatch.elapsedMicroseconds;
}
static void runTests(List list, String groupName,
{bool useRandomPivot = false, bool sortFromConstructor = false}) {
// CONTROL
var controlElapsedTimeMicroSec = 0;
controlElapsedTimeMicroSec = _runControlTest(list);
// END CONTROL
var expectedRecursionCountLogN = log2(list.length).ceil();
var expectedRecursionCountNLogN = list.length * expectedRecursionCountLogN;
var qs = QuickSort();
var stopwatch = Stopwatch();
stopwatch.start();
list =
qs.sort(iList: list, useRandomPivot: useRandomPivot); // METHOD TO TEST
stopwatch.stop();
var elapsedTimeMicroSec = stopwatch.elapsedMicroseconds;
var recursionCount = qs
.recurseCount; //NOTE (BUG in Dart?): unable to get the recurseCount correctly from within this class/method
group(groupName, () {
var testSubject =
'Time Taken: ${controlElapsedTimeMicroSec} microseconds';
var reason =
'The built in sort took ${controlElapsedTimeMicroSec} microseconds, while the test took ${elapsedTimeMicroSec}.';
test(testSubject, () {
expect(
elapsedTimeMicroSec, lessThanOrEqualTo(controlElapsedTimeMicroSec),
reason: reason);
});
testSubject =
'Time Complexity: ${recursionCount} vs ${expectedRecursionCountNLogN}';
reason =
'Time Complexity of ${recursionCount} is greater than either range (LogN) ${expectedRecursionCountLogN} or (N*LogN) ${expectedRecursionCountNLogN}';
test(testSubject, () {
expect(recursionCount, lessThanOrEqualTo(expectedRecursionCountNLogN),
reason: reason);
});
});
}
}
void main() {
var min = 0;
var len = 1000000;
var max = len;
var originalList = getRandomIntList(min: min, max: max, len: len);
var list = List.from(originalList);
// BUG? When called within this Tester.runTests the sort method does NOT return the correct recurseCount
Tester.runTests(list, 'UNSORTED_RIGHT_PIVOT', useRandomPivot: false);
list = List.from(originalList);
var qs = QuickSort();
// When called directly from main() the sort method DOES return the correct recurseCount
var stopwatch = Stopwatch()..start();
qs.sort(iList: list, useRandomPivot: true); //METHOD TO TEST
stopwatch.stop();
group('UNSORTED_RANDOM_PIVOT', () {
test('Time Taken: ${stopwatch.elapsedMicroseconds} microseconds', () {
expect(stopwatch.elapsedMicroseconds,
lessThanOrEqualTo(Tester._runControlTest(originalList)));
});
var nLogN = (list.length * (log2(list.length).ceil()));
test('Time Complexity: ${qs.recurseCount} vs $nLogN', () {
expect(qs.recurseCount, lessThanOrEqualTo(nLogN));
});
});
}
Let's say I have a list of date object. I would like to separate them into a new list by month, and this list will add into another list. Which eventually become a List<List<DateTime>>.
{
[2020-12-25],
[2020-12-25],
[2020-11-24],
[2020-10-23]
}
to
[
[2020-12-25,2020-12-25],
[2020-11-24],
[2020-10-23]
]
What I have tried in sandbox
void test(List<DateTime> datelist) {
List<List<DateTime> mainList = [];
datelist.forEach((date){
if(mainlist.isEmpty == true) {
List<DateTime> temp = [];
temp.add(date);
mainList.add(temp);
} else {
mainList.forEach((monthList) {
if(monthList.first.month == date.month) {
monthList.add(date);
} else {
List<DateTime> temp = [];
temp.add(date);
mainList.add(temp);
}
});
}
});
}
But hit error: Uncaught Error: concurrent modification during iteration: Instance of 'JSArray<List<DateTime>>.
Something like this should work:
void main() async {
var dates = <DateTime>[
DateTime(2020, 12, 25),
DateTime(2020, 12, 25),
DateTime(2020, 11, 24),
DateTime(2020, 10, 23)];
var monthMap = Map<int, List<DateTime>>();
for (final date in dates) {
var list = monthMap[date.month];
if (list == null) {
list = [date];
monthMap[date.month] = list;
} else {
list.add(date);
}
}
List<List<DateTime>> mainList = monthMap.values.toList();
print("[" + mainList.map((e) {
return "[${e.map((d) => "${d.year}-${d.month}-${d.day}").join(", ")}]";
}).join(", ") + "]");
}
Let's say we have a name set to "Ben Bright". I want to output to the user "BB", with the first characters of each word. I tried with the split() method, but I failed to do it with dart.
String getInitials(bank_account_name) {
List<String> names = bank_account_name.split(" ");
String initials;
for (var i = 0; i < names.length; i++) {
initials = '${names[i]}';
}
return initials;
}
Allow me to give a shorter solution than the other mentioned:
void main() {
print(getInitials('')); //
print(getInitials('Ben')); // B
print(getInitials('Ben ')); // B
print(getInitials('Ben Bright')); // BB
print(getInitials('Ben Bright Big')); // BB
}
String getInitials(String bank_account_name) => bank_account_name.isNotEmpty
? bank_account_name.trim().split(' ').map((l) => l[0]).take(2).join()
: '';
The take(2) part ensures we only take up to two letters.
EDIT (7th October 2021):
Or if we must be able to handle multiple spaces between the words we can do (thanks #StackUnderflow for notice):
void main() {
print(getInitials('')); //
print(getInitials('Ben')); // B
print(getInitials('Ben ')); // B
print(getInitials('Ben Bright')); // BB
print(getInitials('Ben Bright Big')); // BB
print(getInitials('Ben Bright Big')); // BB
}
String getInitials(String bankAccountName) => bankAccountName.isNotEmpty
? bankAccountName.trim().split(RegExp(' +')).map((s) => s[0]).take(2).join()
: '';
Notice that split takes a RegExp(' +') compared to the original solution.
Just a slight modification since you only need the first letters
String getInitials(bank_account_name) {
List<String> names = bank_account_name.split(" ");
String initials = "";
int numWords = 2;
if(numWords < names.length) {
numWords = names.length;
}
for(var i = 0; i < numWords; i++){
initials += '${names[i][0]}';
}
return initials;
}
Edit:
You can set the value of num_words to print the intials of those many words.
If the bank_account_name is a 0 letter word, then return an empty string
If the bank_account_name contains lesser words than num_words, print the initials of all the words in bank_account_name.
var string = 'William Henry Gates';
var output = getInitials(string: string, limitTo: 1); // W
var output = getInitials(string: string, limitTo: 2); // WH
var output = getInitials(string: string); // WHG
String getInitials({String string, int limitTo}) {
var buffer = StringBuffer();
var split = string.split(' ');
for (var i = 0 ; i < (limitTo ?? split.length); i ++) {
buffer.write(split[i][0]);
}
return buffer.toString();
}
A more general solution can be found below. It takes care of empty strings, single word strings and situations where anticipated word count is less than actual word count:
static String getInitials(String string, {int limitTo}) {
var buffer = StringBuffer();
var wordList = string.trim().split(' ');
if (string.isEmpty)
return string;
// Take first character if string is a single word
if (wordList.length <= 1)
return string.characters.first;
/// Fallback to actual word count if
/// expected word count is greater
if (limitTo != null && limitTo > wordList.length) {
for (var i = 0; i < wordList.length; i++) {
buffer.write(wordList[i][0]);
}
return buffer.toString();
}
// Handle all other cases
for (var i = 0; i < (limitTo ?? wordList.length); i++) {
buffer.write(wordList[i][0]);
}
return buffer.toString();
}
Edit:
I actually use this for CircleAvatars with no images in my projects.
I used CopsOnRoad solution but I was getting the following error.
RangeError (index): Invalid value: Only valid value is 0: 1
So I modified it to
String getInitials(String string, [int limitTo = 2]) {
if (string == null || string.isEmpty) {
return '';
}
var buffer = StringBuffer();
var split = string.split(' ');
//For one word
if (split.length == 1) {
return string.substring(0, 1);
}
for (var i = 0; i < (limitTo ?? split.length); i++) {
buffer.write(split[i][0]);
}
return buffer.toString();
}
Here are some tests in case you are interested
void main() {
group('getInitials', () {
test('should process one later word name correctly', () {
final result = getInitials('J');
expect(result, 'J');
});
test('should process one word name correctly', () {
final result = getInitials('John');
expect(result, 'J');
});
test('should process two word name correctly', () {
final result = getInitials('John Mamba');
expect(result, 'JM');
});
test('should process more than two word name correctly', () {
final result = getInitials('John Mamba Kanzu');
expect(result, 'JM');
});
test('should return empty string when name is null', () {
final result = getInitials(null);
expect(result, '');
});
test('should return empty string when name is empty', () {
final result = getInitials('');
expect(result, '');
});
});
}
String getInitials(full_name) {
List<String> names = full_name.split(" ");
print("org::: $full_name");
print("list ::: $names");
print("Substring ::: ${names[0].substring(0,1)}");
String initials = "";
int numWords = 2;
numWords = names.length;
for(var i = 0; i < numWords; i++)
{
initials += '${names[i].substring(0,1)}';
print("the initials are $initials");
}
return initials;
}
On Nov, 2022
Working solution using Regex:
String getInitials(String string) => string.isNotEmpty
? string.trim().split(RegExp(' +')).map((s) => s[0]).join()
: '' ;
I'm using frida on an APK and I'm trying to print out which function is being called, and especially which parameters are sent to it. My environment is set correctly as I can print out classes, and perform various actions in accordance to the docs.
This is the closest i've found:
https://codeshare.frida.re/#razaina/get-a-stack-trace-in-your-hook/
But the code gives out errors (ThreadDef undefined and so on)
And the frida docs didn't help me get where I'm trying to.
Any guidance? suggestions? help?
The code from codeshare#razaina has bugs which can be easily fixed (threadef != ThreadDef)
var printBacktrace = function () {
Java.perform(function() {
var JLog = Java.use('android.util.Log'), JException = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
console.warn(JLog.getStackTraceString(JException.$new()));
});
};
Just call printBacktrace() w/e you want to see who called your hooked function.
If you want to hook all the methods of Java class you can use this snippet;
var Color = {
RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", Green: "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01",
Light: {
Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", Green: "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"
}
};
/**
*
* #param input.
* If an object is passed it will print as json
* #param kwargs options map {
* -l level: string; log/warn/error
* -i indent: boolean; print JSON prettify
* -c color: #see ColorMap
* }
*/
var LOG = function (input, kwargs) {
kwargs = kwargs || {};
var logLevel = kwargs['l'] || 'log', colorPrefix = '\x1b[3', colorSuffix = 'm';
if (typeof input === 'object')
input = JSON.stringify(input, null, kwargs['i'] ? 2 : null);
if (kwargs['c'])
input = colorPrefix + kwargs['c'] + colorSuffix + input + Color.RESET;
console[logLevel](input);
};
var printBacktrace = function () {
Java.perform(function() {
var android_util_Log = Java.use('android.util.Log'), java_lang_Exception = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
LOG(android_util_Log.getStackTraceString(java_lang_Exception.$new()), { c: Color.Gray });
});
};
function traceClass(targetClass) {
var hook;
try {
hook = Java.use(targetClass);
} catch (e) {
console.error("trace class failed", e);
return;
}
var methods = hook.class.getDeclaredMethods();
hook.$dispose();
var parsedMethods = [];
methods.forEach(function (method) {
var methodStr = method.toString();
var methodReplace = methodStr.replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1];
parsedMethods.push(methodReplace);
});
uniqBy(parsedMethods, JSON.stringify).forEach(function (targetMethod) {
traceMethod(targetClass + '.' + targetMethod);
});
}
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf('.');
if (delim === -1)
return;
var targetClass = targetClassMethod.slice(0, delim);
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
LOG({ tracing: targetClassMethod, overloaded: overloadCount }, { c: Color.Green });
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
var log = { '#': targetClassMethod, args: [] };
for (var j = 0; j < arguments.length; j++) {
var arg = arguments[j];
// quick&dirty fix for java.io.StringWriter char[].toString() impl because frida prints [object Object]
if (j === 0 && arguments[j]) {
if (arguments[j].toString() === '[object Object]') {
var s = [];
for (var k = 0, l = arguments[j].length; k < l; k++) {
s.push(arguments[j][k]);
}
arg = s.join('');
}
}
log.args.push({ i: j, o: arg, s: arg ? arg.toString(): 'null'});
}
var retval;
try {
retval = this[targetMethod].apply(this, arguments); // might crash (Frida bug?)
log.returns = { val: retval, str: retval ? retval.toString() : null };
} catch (e) {
console.error(e);
}
LOG(log, { c: Color.Blue });
return retval;
}
}
}
// remove duplicates from array
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
var Main = function() {
Java.perform(function () { // avoid java.lang.ClassNotFoundException
[
// "java.io.File",
'java.net.Socket',
'com.package.MyCustomClass'
].forEach(traceClass);
});
};
Java.perform(Main);
Hi I Got a notnull function for a text field as below
private function valStringNotNull( val:String ) :Boolean
{
if ( String(val).length <= 0 )
{
_errorCode = "StringNull";
return false;
}
_errorCode = "NoError";
return true;
}
and this function is being called here
var pCnt:Number = 0;
_validateParams[pCnt++] = { type: "notNull", input: win.firstNameInput , isSendData:true, dataName:"firstName"};
_validateParams[pCnt++] = { type: "notNull", input: win.lastNameInput, isSendData:true, dataName:"lastName"};
_validateParams[pCnt++] = { type: "noValidation", input: roleCombo, isSendData:true, dataName:"role" };
Selection.setFocus(win.firstNameInput);
and for the not null I defined this way
private function validateCases ( param:Object ) :Boolean
{
_errorObj = param.input || param.input1;
switch( param.type )
{
case "notNull":
return valStringNotNull( param.input.text );
break;
}
}
but as you see as I defined the length should be greater than zero its taking even a space as an input and displaying blank white space in my text field so I got a trim function as below
public function ltrim(input:String):String
{
var size:Number = input.length;
for(var i:Number = 0; i < size; i++)
{
if(input.charCodeAt(i) > 32)
{
return input.substring(i);
}
}
return "";
}
and I need to call this trim function before my not null function so that it trims off all the leftside white space but as I am very new to flash can some one help me how to keep this trim function before the notnull function.Can some one please help me with this please
A function to replace any string as you wish, just combine them!
String.prototype.replace = function(searchStr, replaceStr):String
{
return this.split(searchStr).join(replaceStr);
};
Example:
// initial string with a placeholder
var str:String = '$person is welcome';
// replace $person with 'Flash developer' and trace it
var replacedStr:String = str.replace('$person','Flash developer');
trace(replacedStr);
Why not just change valStringNotNull() as follows?
private function valStringNotNull( val:String ) :Boolean
{
if ( String(ltrim(val)).length <= 0 )
{
_errorCode = "StringNull";
return false;
}
_errorCode = "NoError";
return true;
}