How to reduce a list to earliest/latest DateTime items in Dart- you know the fun type of code problem - flutter

I have a list of DateTime items, I want to get the earliest and latest items in that list for initializing a date range picker with min/max values based on the available data in db.
I have a tough time tracking the "behind the scenes" in loops and the reduce operator. So I'm looking for any help in making this happen efficiently, cause I'm sure I can make this happen inefficiently with 10 loops :P
What I've tried so far, random stuff that obviously is way off. Double loops and looping with reduce and .isbefore or .isAfter. Can't figure this out. My code below is a hodgepodge but it's where I'm at now. I guess another option is to simple order the list by date and take first and last, but I want to see the best solution and I think this is the right place to ask.
This is what I'm trying to apply.
List<DateTime> dateList = [
DateTime(2021, 7, 30),
DateTime(2021, 6, 25),
DateTime(2021, 5, 14),
DateTime(2021, 4, 2),
DateTime(2021, 3, 12),
DateTime(2021, 3, 21)
];
List<DateTime> databaseDateRange() {
var databaseDateRange = <DateTime>[];
for (var item in dateList) {
dateList.reduce((value, element) {
if (value.isAfter(item)) {
databaseDateRange.add(value.xAxis);
}
});
}
return databaseDateRange;
}

print(dateList.reduce((min, e) => e.isBefore(min)? e : min));

In order to get the minimum in the list (i.e earliest date) you can do as #the38amorim said.
I.e
print(dateList.reduce((min, e) => e.isBefore(min) ? e : min));
The above iterates through each one and checks whether the new value is before the saved one, and replaces it if that is the case, otherwise keeps the old one.
For maximum value in the list (i.e latest date), you can conversely do:
print(dateList.reduce((min, e) => e.isAfter(min) ? e : min));
More on reduce here: https://api.dart.dev/stable/2.18.1/dart-core/Iterable/reduce.html

Related

How to Sort List of Week Day names in flutter in ascending order

I have a list of Week Day names in random order, how to sort it in ascending order e.g. Monday, Tuesday, Wednesday …
List
[Friday, Monday, Sunday, Wednesday]
Desired List
[Monday, Wednesday, Friday, Sunday]
I have tried
list.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
The issue you're going to have to overcome is that there is no super easy way to get a listing of the days of the week. However, that isn't actually a difficult issue to deal with if you're only needing to deal with this for english - you can just hardcode it.
Here's an example:
const weekDays = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
];
final positions = weekDays.asMap().map((ind, day) => MapEntry(day, ind));
The positions is slightly more interesting; by calling asMap, I'm converting the list to a map with the keys being the positions (i.e. 0:"Monday", ... 6:"Friday") and then swapping them. This will result in a slightly faster lookup than just using indexOf, although that's realistically probably an unnecessary optimization in this case.
Then to sort, you can just use a list's sort method and pass it a custom comparitor function:
dates.sort((first, second) {
final firstPos = positions[first] ?? 7;
final secondPos = positions[second] ?? 7;
return firstPos.compareTo(secondPos);
});
Putting it all together, this is what it looks like:
const weekDays = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
];
final positions = weekDays.asMap().map((ind, day) => MapEntry(day, ind));
void main() {
final dates = ["Friday", "Monday", "Sunday", "Banana", "Wednesday"];
dates.sort((first, second) {
final firstPos = positions[first] ?? 8;
final secondPos = positions[second] ?? 8;
return firstPos.compareTo(secondPos);
});
print("sorted: $dates");
}
Note that if your data isn't complete sanitized, you might want to normalize it to be all lower case, and do the same for your "weekDays" listing.
To sort the list of days such that it starts from Sunday in ascending order, you can use the sort method from the List class and specify a custom Comparator function that compares the days based on their index in the list. The code for this will be:
https://gist.github.com/tanmoy27112000/f0077c593dd883d93a338261fd9a96a5
This will compare the days provided with the given dayList and give you the required sorted list.
sortedDays can also be modified to sort the given list according to your liking.
you can add mapping data with value ex
const map = { 'Monday': 1, 'Tuesday': 2, 'Wednesday': 3, 'Thursday': 4, 'Friday': 5, 'Saturday': 6, 'Sunday': 7 };
and then you can mapping value and sort it. or you can see in this ask
link

Dart, Compareto two situations in same time on list items

I have formatted dateFormat items, I mean : 11-08, 22-12 "dd-MM" items in a list. I want to sort them from the nearest date to farthest date. I did use compareTo like that (tumKisiler is a list)
'''
tumKisiler.sort((a, b) {
return a.siralamaDogumTarihi
.substring(3, 5)
.compareTo(b.siralamaDogumTarihi.substring(3, 5));
'''
but that is only sort by months. I want to sort by days also.
The output must be look like that :
13-08
26-08
17-09
31-12
How can I do that? I am new here, I hope could explain.

Task list with re-ordering feature using Firebase/Firestore

I want to make a list of tasks that can change their order, but I am not sure how to store this in a database.
I don't want to use array because I have to do some queries further in future.
Here is the screenshot of my database:
I'm trying to make something like Trello where the user adds tasks and can move tasks upward and downward according to their priority. I need to change the position of the tasks in the database as well to maintain the record. I'm unable to understand how to do that in any database. I'm an experienced developer and I have worked with mongodb and firebase but this is something unique for me.
Here is the code to create and get all tasks. When I try to move some task in collection. I maintained an index in each task.
Let's say when I move a task from the position of index 5 to index 2 then I have to edit all the upcoming indexes by +1. Is there some way I can avoid doing this?
Code Sample
class taskManager {
static let shared = taskManager()
typealias TasksCompletion = (_ tasks:[Task],_ error:String?)->Void
typealias SucessCompletion = (_ error:String?)->Void
func addTask(task:Task,completion:#escaping SucessCompletion){
Firestore.firestore().collection("tasks").addDocument(data: task.toDic) { (err) in
if err != nil {
print(err?.localizedDescription as Any)
}
completion(nil)
}
}
func getAllTask(completion:#escaping TasksCompletion){
Firestore.firestore().collection("tasks")
.addSnapshotListener { taskSnap, error in
taskSnap?.documentChanges.forEach({ (task) in
let object = task.document.data()
let json = try! JSONSerialization.data(withJSONObject: object, options: .prettyPrinted)
var taskData = try! JSONDecoder().decode(Task.self, from: json)
taskData.id = task.document.documentID
if (task.type == .added) {
Task.shared.append(taskData)
}
if (task.type == .modified) {
let index = Task.shared.firstIndex(where: { $0.id == taskData.id})!
Task.shared[index] = taskData
}
})
if error == nil{
completion(Task.shared,nil)
}else{
completion([],error?.localizedDescription)
}
}
}
}
I think the question you're trying to ask about is more about database design.
When you want to be able to keep order with a group of items while being able to reorder them you will need a column to keep the order.
You run into an issue when you try to order them if they are sequentially ordered.
Example
For example if you wanted to move Item1 behind Item4:
Before
An item with an ordering index.
1. Item1, order: 1
2. Item2, order: 2
3. Item3, order: 3
4. Item4, order: 4
5. Item5, order: 5
6. Item6, order: 6
After
Problem: we had to update every record between the item being moved and where it was placed.
Why this is a problem: this is a Big O(n) - for every space we move we have to update that many records. As you get more tasks this becomes more of an issue as it will take longer and not scale well. It would be nice to have a Big O(1) where we have a constant amount of changes or as few as possible.
1. Item2, order: 1 - Updated
2. Item3, order: 2 - Updated
3. Item4, order: 3 - Updated
4. Item1, order: 4 - Updated
5. Item5, order: 5
6. Item6, order: 6
Possible Solution #1 (OK Maybe?) - Spacing
You could try to come up with a crafty method where you try to space the order numbers out so that you have holes that can be filled without updating multiple records.
This could get tricky though, and you may think, "Why not store Item1 at order: 4.5" I added a related question below that goes into that idea and why you should avoid it.
You may be able to verify the safety of the order client side and avoid hitting the database to determine the new order ID of the move.
This also has limitations as you may have to rebalance the spacing or maybe you run out of numbers to items. You may have to check for a conflict and when a conflict arises you perform a rebalance on everything or recursively the items around the conflict making sure that other balancing updates don't cause more conflicts and that additional conflicts are resolved.
1. Item2, order: 200
2. Item3, order: 300
3. Item4, order: 400
4. Item1, order: 450 - Updated
5. Item5, order: 500
6. Item6, order: 600
Possible Solution #2 (Better) - Linked Lists
As mentioned in the related link below you could use a data structure like a linked list. This retains a constant amount of changes to update so it is Big O(1). I will go into a linked list a bit in case you haven't played with the data structure yet.
As you can see below this change only required 3 updates, I believe the max would be 5 as shown in Expected Updates. You may be thinking, "Well it took about that many with the first original problem/example!" The thing is that this will always be a max of 5 updates compared to the possibility of thousands or millions with the original approach [Big O(n)].
1. Item2, previous: null, next: Item3 - Updated // previous is now null
2. Item3, previous: Item2, next: Item4
3. Item4, previous: Item3, next: Item1 - Updated // next is now Item1
4. Item1, previous: Item4, next: Item5 - Updated // previous & next updated
5. Item5, previous: Item1, next: Item4 - Updated // previous is now Item1
6. Item6, previous: Item6, next: null
Expected Updates
Item being moved (previous, next)
Old previous item's next
Old next item's previous
New previous item's next
New next item's previous
Linked Lists
I guess I used a double linked list. You probably could get away with just using a single linked list where it doesn't have a previous attribute and only a next instead.
The idea behind a linked list is to think of it a chain link, when you want to move one item you would decouple it from the link in front of it and behind it, then link those links together. Next you would open up where you would want to place it between, now it would have the new links on each side of it, and for those new links they would now be linked to the new link instead of each other.
Possible Solution #3 - Document/Json/Array Storage
You said you want to stay away from arrays, but you could utilize document storage. You could still have a searchable table of items, and then each collection of items would just have an array of item id/references.
Items Table
- Item1, id: 1
- Item2, id: 2
- Item3, id: 3
- Item4, id: 4
- Item5, id: 5
- Item6, id: 6
Item Collection
[2, 3, 4, 1, 5, 6]
Related Question(s)
Storing a reorderable list in a database
Resources on Big O
A guide on Big O
More on Big O
Wiki Big O
Other Considerations
Your database design will depend on what you're trying to accomplish. Can items belong to multiple boards or users?
Can you offload some ordering to the client side and allow it to tell the server what the new order is? You should still avoid inefficient ordering algorithms on the client side, but you can get them to do some of the dirty work if you trust them and don't have any issues with data integrity if multiple people are working on the same items at the same time (those are other design problems, that may or may not be related to the DB, depending on how you handle them.)
I was stuck on the same problem for a long time. The best solution I found was to order them Lexicographically.
Trying to manage a decimal rank (1, 2, 3, 4...) runs into a lot of problems that are all mentioned in other answers on this question. Instead, I store the rank as a string of characters ('aaa', 'bbb', 'ccc'...) and I use the character codes of the characters in the strings to find a spot between to ranks when adjustments are made.
For example, I have:
{
item: "Star Wars",
rank: "bbb"
},
{
item: "Lord of the Rings",
rank: "ccc"
},
{
item: "Harry Potter",
rank: "ddd"
},
{
item: "Star Trek",
rank: "eee"
},
{
item: "Game of Thrones",
rank: "fff"
}
Now I want to move "Game of Thrones" to the third slot, below "Lord of the Rings" ('ccc') and above "Harry Potter" ('ddd').
So I use the character codes of 'ccc' and 'ddd' to mathematically find the average between the two strings; in this case, that ends up being 'cpp' and I'll update the document to:
{
item: "Game of Thrones",
rank: "cpp"
}
Now I have:
{
item: "Star Wars",
rank: "bbb"
},
{
item: "Lord of the Rings",
rank: "ccc"
},
{
item: "Game of Thrones",
rank: "cpp"
},
{
item: "Harry Potter",
rank: "ddd"
},
{
item: "Star Trek",
rank: "eee"
}
If I run out of room between two ranks, I can simply add a letter to the end of the string; so, between 'bbb' and 'bbc', I can insert 'bbbn'.
This is a benefit over decimal ranking.
Things to be aware of
Do not assign 'aaa' or 'zzz' to any item. These need to be withheld to easily allow for moving items to the top or bottom of the list. If "Star Wars" has rank 'aaa' and I want to move something above it, there would be problems. Solvable problems, but this is easily avoided if you start at rank 'bbb'. Then, if you want to move something above the top rank, you can simply find the average between 'bbb' and 'aaa'.
If your list gets reshuffled frequently, it would be good practice to periodically refresh the rankings. If things are moved to the same spot in a list thousands of times, you may get a long string like 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbn'. You may want to refresh the list when a string gets to be a certain length.
Implementation
The algorithm and an explanation of the functions used to achieve this effect can be found here. Credit for this idea goes to the author of that article.
The code I use in my project
Again, credit for this code goes to the author of the article I linked above, but this is the code I have running in my project to find the average between two strings. This is written in Dart for a Flutter app
import 'dart:math';
const ALPHABET_SIZE = 26;
String getRankBetween({String firstRank, String secondRank}) {
assert(firstRank.compareTo(secondRank) < 0,
"First position must be lower than second. Got firstRank $firstRank and second rank $secondRank");
/// Make positions equal
while (firstRank.length != secondRank.length) {
if (firstRank.length > secondRank.length)
secondRank += "a";
else
firstRank += "a";
}
var firstPositionCodes = [];
firstPositionCodes.addAll(firstRank.codeUnits);
var secondPositionCodes = [];
secondPositionCodes.addAll(secondRank.codeUnits);
var difference = 0;
for (int index = firstPositionCodes.length - 1; index >= 0; index--) {
/// Codes of the elements of positions
var firstCode = firstPositionCodes[index];
var secondCode = secondPositionCodes[index];
/// i.e. ' a < b '
if (secondCode < firstCode) {
/// ALPHABET_SIZE = 26 for now
secondCode += ALPHABET_SIZE;
secondPositionCodes[index - 1] -= 1;
}
/// formula: x = a * size^0 + b * size^1 + c * size^2
final powRes = pow(ALPHABET_SIZE, firstRank.length - index - 1);
difference += (secondCode - firstCode) * powRes;
}
var newElement = "";
if (difference <= 1) {
/// add middle char from alphabet
newElement = firstRank +
String.fromCharCode('a'.codeUnits.first + ALPHABET_SIZE ~/ 2);
} else {
difference ~/= 2;
var offset = 0;
for (int index = 0; index < firstRank.length; index++) {
/// formula: x = difference / (size^place - 1) % size;
/// i.e. difference = 110, size = 10, we want place 2 (middle),
/// then x = 100 / 10^(2 - 1) % 10 = 100 / 10 % 10 = 11 % 10 = 1
final diffInSymbols =
difference ~/ pow(ALPHABET_SIZE, index) % (ALPHABET_SIZE);
var newElementCode = firstRank.codeUnitAt(secondRank.length - index - 1) +
diffInSymbols +
offset;
offset = 0;
/// if newElement is greater then 'z'
if (newElementCode > 'z'.codeUnits.first) {
offset++;
newElementCode -= ALPHABET_SIZE;
}
newElement += String.fromCharCode(newElementCode);
}
newElement = newElement.split('').reversed.join();
}
return newElement;
}
There are several approaches you might follow to achieve such functionality.
Approach #1:
You can give your task distant positions instead of continuous position, something like this:
Date: 10 April 2019
Name: "some task name"
Index: 10
...
Index: 20
...
Index: 30
Here are total 3 tasks with position 10, 20, 30. Now lets say you wanted to move third task in the middle, simply change the position to 15, now you have three task with position 10, 15, 20, I am sure you can sort according to the position when getting all tasks from the database, and I also assume that you can get positions of tasks because user will be re arranging the tasks on a mobile app or web app so you can easily get the positions of surrounding tasks and calculate the middle position of surrounding tasks,
Now lets say you wanted to move the first task(which now have possition index 10) in the middle, simply get the positions of surrounding tasks which is 15 and 20 and calculate the middle which is 17.5 ( (20-15)/2=17.5 ) and here you go, now you have positions 15, 17.5, 20
Someone said there is infinity between 1 and 2 so you are not going to run our of numbers I think, but still of you think you will run out of division soon, you can increase the difference and you can make it 100000...00 instead of 10
Approach #2:
You can save all of your tasks in the same document instead of sperate document in stratified json form, something like this:
Tasks: [ {name:"some name",date: "some date" },{name:"some name",date: "some date"},{name:"some name",date: "some date" } ]
By doing this you will get all task at once on the screen and you will parse the json as local array, when user rearrange the task you will simply change the position of that array element locally and save the stratified version of the tasks in database as well, there are some cons of this approach, if you are using pagination it might be difficult to do so but hopefully you will not be using the pagination in task management app and you probably wanted to show all task on the scree at the same time just like Trello does.

How to put numbers into an array and sorted by most frequent number in java

I was given this question on programming in java and was wondering what would be the best way of doing it.
The question was on the lines of:
From the numbers provided, how would you in java display the most frequent number. The numbers was: 0, 3, 4, 1, 1, 3, 7, 9, 1
At first I am thinking well they should be in an array and sorted first then maybe have to go through a for loop. Am I on the right lines. Some examples will help greatly
If the numbers are all fairly small, you can quickly get the most frequent value by creating an array to keep track of the count for each number. The algorithm would be:
Find the maximum value in your list
Create an integer array of size max + 1 (assuming all non-negative values) to store the counts for each value in your list
Loop through your list and increment the count at the index of each value
Scan through the count array and find the index with the highest value
The run-time of this algorithm should be faster than sorting the list and finding the longest string of duplicate values. The tradeoff is that it takes up more memory if the values in your list are very large.
With Java 8, this can be implemented rather smoothly. If you're willing to use a third-party library like jOOλ, it could be done like this:
List<Integer> list = Arrays.asList(0, 3, 4, 1, 1, 3, 7, 9, 1);
System.out.println(
Seq.seq(list)
.grouped(i -> i, Agg.count())
.sorted(Comparator.comparing(t -> -t.v2))
.map(t -> t.v1)
.toList());
(disclaimer, I work for the company behind jOOλ)
If you want to stick with the JDK 8 dependency, the following code would be equivalent to the above:
System.out.println(
list.stream()
.collect(Collectors.groupingBy(i -> i, Collectors.counting()))
.entrySet()
.stream()
.sorted(Comparator.comparing(e -> -e.getValue()))
.map(e -> e.getKey())
.collect(Collectors.toList()));
Both solutions yield:
[1, 3, 0, 4, 7, 9]

Adding days to a date using std.datetime

Why are you not allowed to add days to a date in std.datetime? You can add months and years, but not days.
Recently I had to calculate a date for Easter Sunday, then I had to calculate related holidays (Ascension, Pentecost, Trinity, Corpus) by adding a certain number of days (39, 10, 7, 4) to the last date.
I ended up using dayOfYear:
date.dayOfYear(date.dayOfYear + offset);
This worked well, but only because I remained within the same year. What if I have to add 50 days to, say, dec 28?
Is there an easy way of doing this that I have overlooked?
You can use Duration from core.time.
Importing std.datetime will import core.time, so you can use it directly as follows.
import std.stdio, std.datetime;
void main() {
auto date = Date(2013, 12, 28);
writefln("%s + %s = %s", date, 10.days(), date + 10.days());
}
BTW, days() is an alias to dur!"days"() which constructs a Duration struct.
Check the documentation of core.time http://dlang.org/phobos/core_time.html for more information.
If you haven't read this article on std.datetime yet, then you probably should, as it will probably answer most basic questions that you have for how to use it.
But in general, core.time.Duration is what you should be using for adding and subtracting units from any of the time point types in std.datetime (SysTime, DateTime, Date, or TimeOfDay). So, you get code like
auto date = Date(2012, 12, 21);
date += dur!"days"(50);
or
auto date = Date(2012, 12, 21);
date += days(50);
(The templated dur function is the generic way to generate a Duration, but it has aliases for each of the units that it supports, so stuff like seconds(5) or 22.minutes() will work as well).
The add function exists for "months" and "years", because a Duration can't hold months or years (because you can't convert between them and smaller units without a specific date), and there needs to be a way to add months or years to a time point. Also, there's the question of what to do when you add or subtract a month or year to/from a date, and the month that it moves to doesn't include that day, so add accepts AllowDayOverflow in order to control that (which wouldn't be necessary with smaller units).
auto d3 = Date(2000, 2, 29);
d3.add!"years"(1);
assert(d3 == Date(2001, 3, 1));
auto d4 = Date(2000, 2, 29);
d4.add!"years"(1, AllowDayOverflow.no);
assert(d4 == Date(2001, 2, 28));
But add doesn't accept any other units, because you can simply use the normal arithmetic operations with a Duration. Also, subtracting two time points will result in a Duration.
assert(Date(2012, 12, 5) - Date(2002, 11, 17) == dur!"days"(3671));
assert(Date(2012, 12, 5) - dur!"days"(3671) == Date(2002, 11, 17));
Unlike add, roll accepts all of the units in the type rather than just "months" and "years", but that's because it's doing a different operation from +, and so adding or subtracting a Duration won't work (as that already adds or subtracts). Rather roll adds to a specific unit without adding to the others.
auto d = Date(2010, 1, 1);
d.roll!"days"(33);
assert(d == Date(2010, 1, 3));
You can useroll method:
date.roll!"days"(50);
I did overlook it: you can use dayOfGregorianCal:
import std.stdio, std.datetime;
void main() {
auto d = Date(2012, 12, 28);
writeln(d); // 2012-Dec-28
d.dayOfGregorianCal(d.dayOfGregorianCal + 50);
writeln(d); // 2013-Feb-16
}