Increment enum or set enum value by index - dart

I have an enum type:
enum Day {sunday, monday, tuesday...}
and a variable:
Day day = Day.sunday;
I would like to be able to increment the day. something like this:
day++; // this would be wonderful
day = Day.getByIndex(day.index + 1); // something like this can also work
But I couldn't find anything like this. Is the only option to use a switch on all days?

DartPad example
enum Day {sunday, monday, tuesday}
void main() {
var day = Day.sunday;
print(day);
print(Day.values[day.index + 1]);
}
Dart enums are known to not be very powerful. I guess they'll get some overhaul eventually.

Related

Find difference between two timestamps in Dart

I'm trying to find out the difference between two timestamps in Hours, Minutes, and Seconds and have managed to chalk out the below code to achieve the same. However, I don't seem to be getting the correct output. Can anyone please tell me where it is that I'm going wrong?
import 'package:intl/intl.dart';
void main() {
String date = '2022-12-05 23:02:20';
var stored =
DateTime.parse(DateFormat('yyyy-mm-dd hh:mm:ss.ms').format(DateTime.parse(date)));
var now = DateTime.now();
var difference = now.difference(stored).inSeconds;
Duration duration = Duration(seconds: difference);
print('VALUE: $stored');
print('CURRENT TIME: $now');
print(stored.runtimeType);
print('HOURS: ${duration.inHours}');
print('MINUTES: ${duration.inMinutes}');
print('SECONDS: ${duration.inSeconds}');
}
This here is the output that I'm getting:
VALUE: 2022-02-05 11:02:20.220
CURRENT TIME: 2022-12-05 23:44:08.827
DateTime
HOURS: 7284
MINUTES: 437081
SECONDS: 26224908
Common mathematics suggests that the difference between 2022-12-05 23:44:08.827 and 2022-02-05 11:02:20.220 should produce 42 minutes and not 437081. Also, this was written on Dartpad
There are a few things wrong:
DateTime.parse(DateFormat('yyyy-mm-dd hh:mm:ss.ms').format(DateTime.parse(date))); makes no sense. You're taking a String representation of a date/time, parsing it with DateTime.parse to get a DateTime object, then using DateFormat to convert that back to a String so that you can call DateTime.parse on it again. Just use DateTime.parse or DateFormat.parse once.
As Ben explained, you're using the wrong DateFormat pattern. MM should be used for the month number; mm should be used for minutes.
.ms in your DateFormat pattern is also wrong; that means minutes and seconds, not milliseconds. You should use S for fractional seconds. But since you're just parsing a date/time string in an ISO format, you don't need DateFormat at all.
var difference = now.difference(stored).inSeconds;
Duration duration = Duration(seconds: difference);
This also doesn't make much sense. now.difference(stored) already returns a Duration object. There's no point in converting a Duration to a number of seconds back to another a Duration unless you're trying to explicitly discard any fractional seconds.
Common mathematics suggests that the difference between 2022-12-05 23:44:08.827 and 2022-02-05 11:02:20.220 should produce 42 minutes and not 437081.
You seem to expect that Duration.inMinutes should return the minutes component of the duration, but inMinutes returns the total number of minutes. For example, Duration(hours: 1, minutes: 2).inMinutes will return 62, not 2. If you instead want the minutes component, you will need to use something like duration.inMinutes.remainder(60). Same thing applies for Duration.inSeconds.
Here is an adjusted version:
void main() {
String date = '2022-12-05 23:02:20';
var stored = DateTime.parse(date);
var now = DateTime.now();
var duration = now.difference(stored);
print('VALUE: $stored');
print('CURRENT TIME: $now');
print(stored.runtimeType);
print('HOURS: ${duration.inHours}');
print('MINUTES: ${duration.inMinutes.remainder(60)}');
print('SECONDS: ${duration.inSeconds.remainder(60)}');
}
which for me outputs:
VALUE: 2022-12-05 23:02:20.000
CURRENT TIME: 2022-12-05 12:12:37.693
DateTime
HOURS: -10
MINUTES: -49
SECONDS: -42
Note that since the above code currently is subtracting a later time from an earlier time, the resulting difference is a negative Duration, so the output might look a little weird.
You should be using MM instead of mm when parsing the date.
Fixed example:
import 'package:intl/intl.dart';
void main() {
String date = '2022-12-05 23:02:20';
var stored =
DateTime.parse(DateFormat('yyyy-MM-dd hh:mm:ss.ms').format(DateTime.parse(date)));
var now = DateTime.now();
var difference = now.difference(stored).inSeconds;
Duration duration = Duration(seconds: difference);
print('VALUE: $stored');
print('CURRENT TIME: $now');
print(stored.runtimeType);
print('HOURS: ${duration.inHours}');
print('MINUTES: ${duration.inMinutes}');
print('SECONDS: ${duration.inSeconds}');
}
Output (13:29 EST timezone):
VALUE: 2022-12-05 11:02:20.220
CURRENT TIME: 2022-12-05 13:29:06.916
DateTime
HOURS: 2
MINUTES: 146
SECONDS: 8806

How to print value of an enum? [duplicate]

Before enums were available in Dart I wrote some cumbersome and hard to maintain code to simulate enums and now want to simplify it. I need to get the name of the enum as a string such as can be done with Java but cannot.
For instance little test code snippet returns 'day.MONDAY' in each case when what I want is 'MONDAY"
enum day {MONDAY, TUESDAY}
print( 'Today is $day.MONDAY');
print( 'Today is $day.MONDAY.toString()');
Am I correct that to get just 'MONDAY' I will need to parse the string?
Dart 2.7 comes with new feature called Extension methods. Now you can write your own methods for Enum as simple as that!
enum Day { monday, tuesday }
extension ParseToString on Day {
String toShortString() {
return this.toString().split('.').last;
}
}
main() {
Day monday = Day.monday;
print(monday.toShortString()); //prints 'monday'
}
Bit shorter:
String day = theDay.toString().split('.').last;
Update Dart 2.15:
enum Day {
monday,
tuesday,
}
You can use name property on the enum.
String monday = Day.monday.name; // 'monday'
Old solution:
1. Direct way:
var dayInString = describeEnum(Day.monday);
print(dayInString); // prints 'monday'
2. Using Extension:
extension DayEx on Day {
String get name => describeEnum(this);
}
You can use it like:
void main() {
Day monday = Day.monday;
print(monday.name); // 'monday'
}
It used to be correct that the only way to get the source name
of the enum value was through the toString method which returns "day.MONDAY", and not the more useful "MONDAY".
Since Dart 2.15, enums have exposed a extension getter which returns just the source name, so day.MONDAY.name == "MONDAY".
Since Dart 2.17, you can also add your own members to enum declarations, and choose to provide another name for
a value than justits source name, e.g., with a more appropriate capitalization:
enum Day {
monday("Monday"),
tuesday("Tuesday"),
// ...
saturday("Saturday");
sunday("Sunday");
final String name;
Day(this.name);
// And other members too.
bool get isWorkday => index < saturday.index;
}
Then you get Day.sunday.name == "Sunday" (hiding the extension name getter which would return "sunday").
Before these features, you could only get the name from the toString string, extracting the rest of the string as:
day theDay = day.MONDAY;
print(theDay.toString().substring(theDay.toString().indexOf('.') + 1));
which was admittedly hardly convenient.
Another way to get the enum name as a string, one which is shorter, but also less efficient because it creates an unnecessary string for first part of the string too, was:
theDay.toString().split('.').last
If performance doesn't matter, that's probably what I'd write, just for brevity.
If you want to iterate all the values, you can do it using day.values:
for (day theDay in day.values) {
print(theDay);
}
Simplest way to get the name of an enum is a standard method from the flutter/foundation.dart
describeEnum(enumObject)
enum Day {
monday, tuesday, wednesday, thursday, friday, saturday, sunday
}
void validateDescribeEnum() {
assert(Day.monday.toString() == 'Day.monday');
assert(describeEnum(Day.monday) == 'monday');
}
enum day {MONDAY, TUESDAY}
print( 'Today is ${describeEnum(day.MONDAY)}' );
console output: Today is MONDAY
There is a more elegant solution:
enum SomeStatus {
element1,
element2,
element3,
element4,
}
const Map<SomeStatus, String> SomeStatusName = {
SomeStatus.element1: "Element 1",
SomeStatus.element2: "Element 2",
SomeStatus.element3: "Element 3",
SomeStatus.element4: "Element 4",
};
print(SomeStatusName[SomeStatus.element2]) // prints: "Element 2"
Sometimes I need to separate ui-value and real-value, so I defined keys and values using Map. This way, we have more flexiblity. And by using extension (since Dart 2.7), I made a method to read its key and value.
enum Status {
progess,
done,
}
extension StatusExt on Status {
static const Map<Status, String> keys = {
Status.progess: 'progess',
Status.done: 'done',
};
static const Map<Status, String> values = {
Status.progess: 'In Progress',
Status.done: 'Well done',
};
String get key => keys[this];
String get value => values[this];
// NEW
static Status fromRaw(String raw) => keys.entries
.firstWhere((e) => e.value == raw, orElse: () => null)
?.key;
}
// usage 1
Status status = Status.done;
String statusKey = status.key; // done
String statusValue = status.value; // Well done
// usage 2 (easy to make key and value list)
List<Status> statuses = Status.values;
List<String> statusKeys = statuses.map((e) => e.key).toList();
List<String> statusValues = statuses.map((e) => e.value).toList();
// usage 3. create Status enum from string.
Status done1 = StatusExt.fromRaw('done') // Status.done
Status done2 = StatusExt.fromRaw('dude') // null
With Dart 2.17 we now have general support for members on enums. That means we can add fields holding state, constructors that set that state, methods with functionality, and even override existing members.
Example:
enum Day {
MONDAY("Monday"),
TUESDAY("Tuesday");
const Day(this.text);
final String text;
}
Output:
void main() {
const day = Day.MONDAY;
print(day.text); /// Monday
}
For above functionality override dart version like below which target 2.17 and greater
environment:
sdk: ">=2.17.0 <3.0.0"
I got so over this I made a package:
https://pub.dev/packages/enum_to_string
Also has a handy function that takes enum.ValueOne and parses it to "Value one"
Its a simple little library but its unit tested and I welcome any additions for edge cases.
I use the functions below to get the name of the enum value and, vise versa, the enum value by the name:
String enumValueToString(Object o) => o.toString().split('.').last;
T enumValueFromString<T>(String key, Iterable<T> values) => values.firstWhere(
(v) => v != null && key == enumValueToString(v),
orElse: () => null,
);
When using Dart 2.7 and newer, extension methods would work here (as well as for any other Objects):
extension EnumX on Object {
String asString() => toString().split('.').last;
}
The implementation above is not dependant on the specific enums.
Usage examples:
enum Fruits {avocado, banana, orange}
...
final banana = enumValueFromString('banana', Fruits.values);
print(enumValueToString(banana)); // prints: "banana"
print(banana.asString()); // prints: "banana"
Edit from 2020-04-05: Added nullability checks. values parameter could be Iterable, not necessarily List. Added extensions method implementation. Removed <Fruits> annotation from the example to show that the class name duplication is not required.
I use structure like below:
abstract class Strings {
static const angry = "Dammit!";
static const happy = "Yay!";
static const sad = "QQ";
}
Dart 2.15 includes an extension to make this easy:
enum day {MONDAY, TUESDAY}
print( 'Today is ${day.MONDAY.name}');
Until the changes in https://github.com/dart-lang/sdk/commit/18f37dd8f3db6486f785b2c42a48dfa82de0948b are rolled out to a stable version of Dart, the other clever but more complex answers here are very useful.
One more way:
enum Length {
TEN,
TWENTY,
THIRTY,
NONE,
}
extension LengthValue on Length {
static const _values = [10, 20, 30, 0];
int get value => _values[this.index];
}
since dart 2.15 just use ".name"
enum day {monday, tuesday}
print( 'Today is ${day.monday.name}');
My approach is not fundamentally different, but might be slightly more convenient in some cases:
enum Day {
monday,
tuesday,
}
String dayToString(Day d) {
return '$d'.split('.').last;
}
In Dart, you cannot customize an enum's toString method, so I think this helper function workaround is necessary and it's one of the best approaches. If you wanted to be more correct in this case, you could make the first letter of the returned string uppercase.
You could also add a dayFromString function
Day dayFromString(String s) {
return Day.values.firstWhere((v) => dayToString(v) == s);
}
Example usage:
void main() {
Day today = Day.monday;
print('Today is: ${dayToString(today)}');
Day tomorrow = dayFromString("tuesday");
print(tomorrow is Day);
}
enum day {MONDAY, TUESDAY}
print(day.toString().split('.')[1]);
OR
print(day.toString().split('.').last);
Create a class to help:
class Enum {
Enum._();
static String name(value) {
return value.toString().split('.').last;
}
}
and call:
Enum.name(myEnumValue);
One of the good ways I found in the answer is
String day = theDay.toString().split('.').last;
But I would not suggest doing this as dart provide us a better way.
Define an extension for the enum, may be in the same file as:
enum Day {
monday, tuesday, wednesday, thursday, friday, saturday, sunday
}
extension DayExtension on Day {
String get value => describeEnum(this);
}
You need to do import 'package:flutter/foundation.dart'; for this.
I had the same problem in one of my projects and existing solutions were not very clean and it didn't support advanced features like json serialization/deserialization.
Flutter natively doesn't currently support enum with values, however, I managed to develop a helper package Vnum using class and reflectors implementation to overcome this issue.
Address to the repository:
https://github.com/AmirKamali/Flutter_Vnum
To answer your problem using Vnum, you could implement your code as below:
#VnumDefinition
class Visibility extends Vnum<String> {
static const VISIBLE = const Visibility.define("VISIBLE");
static const COLLAPSED = const Visibility.define("COLLAPSED");
static const HIDDEN = const Visibility.define("HIDDEN");
const Visibility.define(String fromValue) : super.define(fromValue);
factory Visibility(String value) => Vnum.fromValue(value,Visibility);
}
You can use it like :
var visibility = Visibility('COLLAPSED');
print(visibility.value);
There's more documentation in the github repo, hope it helps you out.
Instead of defining extension for every enum, we can define extension on object and get access to .enumValue from any enum.
void main() {
// ❌ Without Extension ❌
print(Countries.Cote_d_Ivoire.toString().split('.').last.replaceAll("_", " ")); // Cote d Ivoire
print(Movies.Romance.toString().split('.').last.replaceAll("_", " ")); //Romance
// ✅ With Extension ✅
print(Countries.Cote_d_Ivoire.enumValue); // Cote d Ivoire
print(Movies.Romance.enumValue); //Romance
}
enum Countries { United_States, United_Kingdom, Germany, Japan, Cote_d_Ivoire }
enum Movies { Romance, Science_Fiction, Romantic_Comedy, Martial_arts }
extension PrettyEnum on Object {
String get enumValue => this.toString().split('.').last.replaceAll("_", " ");
}
With this, you can even define multi-word enum where words are separated by _(underscore) in its name.
As from Dart 2.15, you can get enum value from
print(MyEnum.one.name);
// and for getting enum from value you use
print(MyEnum.values.byName('two');
try this solution:
extension EnumValueToString on Enum {
String valueAsString() {
return describeEnum(this);
}
}
how to used it:
enum.valueAsString()
dart 2.15 is now supporting this
you can just type
print(day.MONDAY.name); //gives you: MONDAY
Since Dart 2.15, we can just do Day.monday.name, where
enum Day { monday, tuesday }
As from Dart version 2.15, you can access the String value of an enum constant using .name:
enum day {MONDAY, TUESDAY}
void main() {
print('Today is ${day.MONDAY.name}');
// Outputs: Today is MONDAY
}
You can read in detail about all the enum improvements in the official Dart 2.15 release blog post.
as of dart 2.15 you can use .name to get the name of enum elements.
enum day {MONDAY, TUESDAY}
print(day.MONDAY.name); // prints MONDAY
Dart version 2.15 has introduced name property on enums.
Example
void main() {
MyEnum.values.forEach((e) => print(e.name));
}
enum MyEnum { value1, Value2, VALUE2 }
Output:
value1
Value2
VALUE2
For those who require enum with values use this approach as Dart doesn't support this:
class MyEnumClass {
static String get KEY_1 => 'value 1';
static String get KEY_2 => 'value 2';
static String get KEY_3 => 'value 3';
...
}
// Usage:
print(MyEnumClass.KEY_1); // value 1
print(MyEnumClass.KEY_2); // value 2
print(MyEnumClass.KEY_3); // value 3
...
And sure you may put whatever types you need.
now with null safety it looks like this
String enumToString(Object? o) => o != null ? o.toString().split('.').last : '';
T? enumFromString<T>(String key, List<T> values) {
try {
return values.firstWhere((v) => key == enumToString(v));
} catch(e) {
return null;
}
}

Use getters and setters to create string from 1-7 integers

I'm trying to take a 1-7 integer value and print out a day for each value with an enum.
I get an error in the class mapping from firestore, "isn't a field in the enclosing class"
So if 1 is passed in then "Monday" is given
if 2 is passed in then "Tuesday" is given
enum _Days {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
class HeadingItem implements ListItem {
String _weekday;
final int time;
final DocumentReference reference;
set day(int weekday) {
var value = _Days.values[weekday - 1].toString();
var idx = value.indexOf(".") + 1;
var result = value.substring(idx, value.length);
_weekday = result;
}
String get day {
return _weekday;
}
HeadingItem.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['day'] != null),
assert(map['time'] != null),
day = map['day'], // 'day' isn't a field in the enclosing class <--- this is the error that im stuck on...
time = map['time'];
HeadingItem.fromSnapshot(DocumentSnapshot snapshot) : this.fromMap(snapshot.data, reference: snapshot.reference);
}
Change
String get day {
return _weekday;
}
to this
String day = _weekday;
Notice that in your code, day is technically a set of methods of this that allows us to work with day as if it were a member of this; day is not actually a member.
Thus, in order for your initializer list to work as you intended it to, it would already need to have access to this in order to provide the functionality you want (for example, to set the value of the internal member _weekday).
However, as mentioned in the Language Tour of Dart in the section on Initializer Lists, the initializer list does not have access to this. Rather we should see the initializer list as values to assign to members of this during the instantiation.

Group by week / month / year in GORM

I have a domain class (minified) as :-
class Expense {
Date dateOfExpense
int amount
}
I am trying to get sum of amount grouped by week/month/ year of expense date.
Referring to 'sqlGroupProjection' method in grails doc http://grails.org/doc/latest/guide/GORM.html,
I tried using following code:-
def results = c {
between("dateOfExpense", fromDate, toDate)
projections {
sqlGroupProjection 'dateOfExpense,sum(amount) as summed',
'MONTH(dateOfExpense)',['date','summed'],[DATE,NUMBER]
}
}
Throws exception:
No such property: DATE for class: grails.orm.HibernateCriteriaBuilder. Stacktrace follows:
Message: No such property: DATE for class: grails.orm.HibernateCriteriaBuilder
Please suggest an approach using sqlGroupProjection method
Create three new numeric fields each for week,month and year in the domain class. These fields won't be mapped to column in the table.
Provide static mapping for the three fields.
static mapping = {
//provide the exact column name of the date field
week formula('WEEK(DATE_OF_EXPENSE)')
month formula('MONTH(DATE_OF_EXPENSE)')
year formula ('YEAR(DATE_OF_EXPENSE)')
}
Now we can group by desired field using
def results = c.list {
between("dateOfExpense", fromDate, toDate)
projections {
switch(groupBy){
case "week":
groupProperty('year')
groupProperty('month')
groupProperty('week')
break;
case "month"
groupProperty('year')
groupProperty('month')
break;
case "year":
groupProperty('year')
break;
}
sum('amount')
}
}
Instead of this
static mapping = {
week formula('WEEK(DATE_OF_EXPENSE)') //provide the exact column name of the date field
month formula('MONTH(DATE_OF_EXPENSE)')
year formula ('YEAR(DATE_OF_EXPENSE)')
}
try this
static mapping = {
week formula: 'WEEK(DATE)'
month formula: 'MONTH(DATE)'
year formula: 'YEAR(DATE)'
}
Try something like
sqlGroupProjection 'MONTH(dateOfExpense) as month, sum(amount) as summed',
'month',['month','summed'],[NUMBER,NUMBER]
This sqlGroupProjection method seems to be poorly supported. Use
def results = c.list {
between("dateOfExpense", fromDate, toDate)
projections {
groupProperty('dateOfExpense')
sum('amount')
}
}
will produce the deserved outcome.
If you want group by the month of the date, see Grails group by date (It totally outweight my answer, actually. But I reach the same solution after trying your code for a long time.)

EF4 using Linq How can i find the date difference in days

I need to check a difference between a given date and current date is less than 365days?
i tried some thing like this.
System.TimeSpan diff = DateTime.UtcNow.Subtract((DateTime)customer.LastValidationDate);
result = (diff.Days < 1);
this doesn't seem to work correct for few dates.
i need to achieve:
if given date and current date difference is less-than or equal to 1 year (365 days) return true
else return false.
try this found at stackoverflow
public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

Resources