I have a list of maps in dart that I would like to sort.
The list I use:
List<myModel> myList;
(with maps inside):
(I created a model called 'myModel', so if I want to get the views: so I can use: myList[index].views to get the views of a map).
I tried this code:
setState(() {
filteredList = SplayTreeMap.from(
myList,
(a, b) => int.parse(myList[a].views)
.compareTo(int.parse(myList[b].views)));
});
(I want to sort the views, hight to low).
But I get this error:
error: A value of type 'SplayTreeMap' can't be assigned to a variable of type 'List<myModel>'.
What am I doing wrong?
Thanks in advance!
EDIT:
I 'query' my list this way.
filteredList = myList
.where((snapshot) => snapshot.views > 0)
.toList();
This works...
Is there a possibility to sort that way?
You cannot pass a List to SplayTreeMap.from(...).
See the documentation:
SplayTreeMap.from(Map other, [int compare(K key1 K key2), bool isValidKey(dynamic potentialKey)])
You need to use a Map or otherwise use SplayTreeMap.fromIterable(...) (documentation):
SplayTreeMap.fromIterable(Iterable iterable, {K key(dynamic element), V value(dynamic element), int compare(K key1 K key2), bool isValidKey(dynamic potentialKey)})
You could implement in the following way:
final filteredList =
SplayTreeMap.fromIterable(
myList,
key: (m) => m,
value: (m) => m.views,
compare: (a, b) =>
int.parse(a.views).compareTo(int.parse(b.views))
);
The above is an example, adjust it to your needs.
EDIT 2:
Given your edit, you could do something like:
final temp = List.from(myList);
temp.sort((a, b) => int.parse(a.views).compareTo(int.parse(b.views)));
filteredList = temp;
Related
I would like to know if for dart there is the possibility to make a condition between functions of a list, for example if it is true to put .where but if it is false not to put anything. Something like this:
final List<dynamic> lists = List<dynamic>.from(
lists2
.map((x) => Model.fromMap(x)) (isNotWhere) ? .first :
.where((e) => (e.deleted == deleted)),
);
Because if not, the only thing I can think of is to first get the list and in another part of the code make the condition like this :
List<dynamic> lists =
List<dynamic>.from(
lists2.map((x) => Model.fromMap(x))
);
if (!isNotWhere) {
lists = lists
.where((e) => !(e.deleted))
.toList();
}
For your example I would do the where always and use the condition inside. Something like
final List<dynamic> lists = List<dynamic>.from(
lists2
.map((x) => Model.fromMap(x))
.where((e) => (isNotWhere || (e.deleted == deleted))),
);
So basically make the where return everything in the case that you don't want to use the where
I am developing a movie app using TMDB.
A total of 3 pieces of data are needed to create a reservation system.
1.MovieList's index
2.date's index
3.seat Satus
I've been trying to process this as a map.
//Seat Status total 200 Seat
List<bool> _seat = [];
for(int nIndex=0; nIndex<200; nIndex++){
_seat.add(false);
}
//Date index(total 7 days) + Seat status
Map _date = Map();
for(int nIndex=0; nIndex<7; nIndex++){
_date[nIndex] = _seat.toList();
}
//TMDB MovieList index
Map _movie = Map();
for(int nIndex=0; nIndex<MoviesList.length; nIndex++){
_movie[nIndex] = _date;
}
The value is entered normally as I want.
However, when one seat data is changed, all seat data in the map is changed.
I noticed that this is because the map and list references are input.
Do you know how to get a value by call by value rather than by call by reference?
seat = List.filled(200, false, growable: true);
date = List.generate(7, (_) => List.from(seat));
movie = List.generate(length, (_) => List.from(date));
My code was like above.
for example
movie[0][0][0] = false; // Change value
print(movie[0][0][0]); // result false
print(movie[1][0][0]); // result false
In this way, the values change together.
In order to make a new list reference with the same values as the previous one, you can call List.from.
So like this:
_date[nIndex] = List.from(_seat);
you can use a similar syntax with a map
_movie[nIndex] = Map.from(_date);
EDIT
It seems like this solution actually does not work, dart can use .from to copy a list, or even to copy a list of lists, but copying a list of a list of lists? That's too much man! It can't handle that, it will only copy up one level. Thankfully, solving this is simple once you know why it happens:
var seat = List.filled(3, false, growable: true);
var date = List.generate(4, (_) => List.from(seat));
var movie = List.generate(5, (_) => List.from(date.map((v) =>
List.from(v))));
movie[0][0][0] = true; // Change value
print(movie[0][0][0]); // true
print(movie[1][0][0]); // false (default value)
For the last one, instead of creating a new list from seat, I am first using map to copy each of the lists on to a new list, and then I am copying those new lists into a new list.
I believe that fixes the issue we were encountering
PS
This is a recommendation, if you don't care, you can stop reading now:
if you want to generate a list of 200 false values, you can use this syntax instead:
List<bool> _seat = List.filled(200, false, growable: true);
which makes for a shorter and more readable way to do the same thing.
When declaring a map, this syntax is preferred:
Map<int, List<bool>> _date = {};
Map<int, Map<int, List<bool>>> _movie = {};
I know it looks a bit gross, but it makes sure you don't add the wrong types to your map
And finally, why are you using a map in the first place? Your map behaves the exact same as a list because the key is an index, and you could generate said list using the generate constructor instead of a for loop, like this:
//Date index(total 7 days) + Seat status
List<List<bool>> _date = List.generate(7, (_) => List.from(_seat));
//TMDB MovieList index
List<List<List<bool>>> _movie = List.generate(
MoviesList.length, (_) => List.from(_date));
Again, the syntax looks gross, that's because of all of the nested lists, probably better off making a class that can store the values in a more neat and type-safe way, but I lack the information to show you how you would write such a class.
I often use .firstWhere((E element) -> bool) -> E in my project. When porting it to support null safety I couldn't cleanly handle a scenario when an element is not found in a List instance.
.firstWhere, .singleWhere and .lastWhere returns E, not E? so when handling a case when a List does not contain required element there's no other way to return null other than casting a whole list from eg. List<String> to List<String?> which makes testing function worried about each element being potentially null, which it can't be. Before null safety I was able to just use orElse: () => null but with null safety orElse have to return element of type E so troublesome casting is required.
Do I have to have a null substitute for each type to use in orElse or are there other methods to make list checking support missing element scenario with null?
You can just use firstWhereOrNull which should work exactly as you expect.
The solution would be to create an extension on the Iterable type:
extension IterableModifier<E> on Iterable<E> {
E? firstWhereOrNull(bool Function(E) test) =>
cast<E?>().firstWhere((v) => v != null && test(v), orElse: () => null);
}
Then use it like this:
final myList = <String?>['A', 'B', null, 'C'];
String? result = myList.firstWhereOrNull((e) => e == 'D');
print(result); // output: null
result = myList.firstWhereOrNull((e) => e == 'A');
print(result); // output: "A"
Try the full example on DartPad
I am trying to use reactive operators to find individual sum of the values emitted by the observable. The end goal is to emit individual sums. The sequence looks something like this. The ones I want to add up are occuring as continuous groups (of varying length) with varying frequency in between the values I want to discard. The ones I want to add have a field which is of type bool and has value true.
-(F,2)-(T,4)-(T,2)-(T,7)-(F,8)-(F,9)-(F,1)-(T,2)-(T,1)-(F,1)-
What have I tried so far:
myObservable.
.Where(x => x.IsItUseful == true)
.Aggregate(0.0, (sum,currentItem) => sum + currentItem.Value)
.Subscribe("NotYet")
This one give back the sum of ALL elements which have been marked as true.
myObservable
.SkipWhile(x => x.IsItUseful == false)
.TakeWhile(x => x.IsItUseful == true)
.Aggregate(0.0, (sum, currentItem) => sum + currentItem.Item3)
.Subscribe("NotYetAgain");
This one gives the sum of the first group only.
Right now I am trying along these lines.
myObservable
.Buffer(myObservable.DistinctUntilChanged(x => x.IsItUseful => true)
.Subscribe("NotSure")
I am still hazy on on BufferBoundary and BufferClosingSelector. I think a new buffer will open once I process a group of valid values. And this new buffer will have values from that point on wards till the end of another valid group. This means that I will pick up some not valid values too before the second group. I haven't been able to find some examples on Buffer with both open and close options getting used. Not sure if this is right approach too.
The final option is that I write an extension method on Buffer and put my custom logic there. But if there is an out of box solution I will prefer that.
There's two primary approaches I would recommend here. One uses Scan, the other uses Buffer/Window. Both of them have edge case problems that are solvable, but need clarity on the problem side.
Here's the Scan solution:
var result = source
.Scan((0, true), (state, value) => (value.IsItUseful ? state.Item1 + value.Value : 0, value.IsItUseful))
.Publish(_tuples =>
_tuples.Zip(_tuples.Skip(1), (oldTuple, newTuple) => (oldTuple, newTuple))
)
.Where(t => t.oldTuple.Item2 == true && t.newTuple.Item2 == false)
.Select(t => t.oldTuple.Item1);
Scan is similar to Aggregate, just more useful: Aggregate will only dump out one value at the end; whereas Scan emits intermediate values. So we track the running sum in there, resetting to 0 when we see a false. The next step (Zip) combines the latest message with its predecessor, so we can figure out whether or not we have to emit: We want to emit if the new flag value is false, but the old flag value is true. We then emit the old sum.
There's an edge case problem here if the last flag value is true: I'm assuming you want to emit on the OnCompleted, but that won't currently happen. Please clarify if that's needed.
Here's the Window solution:
var result2 = source
.Publish(_values => _values
.Window(_values.Select(v => v.IsItUseful).DistinctUntilChanged().Where(b => b == false))
)
.SelectMany(o => o.Where(a => a.IsItUseful).Sum(a => a.Value));
Window by the distinctly new falses, then sum them, similar to what you proposed.
The edge case problem here is that you end up with a leading and tailing 0 if you begin/end with falses (as your sample set does). Removing those would require some clean up as well.
FYI: Window and Buffer are practically the same: They have the same overloads and each group values into "windows". Window returns them as an observable stream, and Buffer holds them into a list which returns when the window closes. For more look here.
Here's runner code if anybody else wants to test this:
public class Message
{
public Message(bool b, int v)
{
IsItUseful = b;
Value = v;
}
public bool IsItUseful { get; set; }
public int Value { get; set; }
}
var values = new List<Message>
{
new Message(false, 2),
new Message(true, 4),
new Message(true, 2),
new Message(true, 7),
new Message(false, 8),
new Message(false, 9),
new Message(false, 1),
new Message(true, 2),
new Message(true, 1),
new Message(false, 1),
};
var source = values.ToObservable();
var result = source
.Scan((0, true), (state, value) => (value.IsItUseful ? state.Item1 + value.Value : 0, value.IsItUseful))
.Publish(_tuples =>
_tuples.Zip(_tuples.Skip(1), (oldTuple, newTuple) => (oldTuple, newTuple))
)
.Where(t => t.oldTuple.Item2 == true && t.newTuple.Item2 == false)
.Select(t => t.oldTuple.Item1);
var result2 = source
.Publish(_values => _values
.Buffer(_values.Select(v => v.IsItUseful).DistinctUntilChanged().Where(b => b == false))
)
.Select(o => o.Where(a => a.IsItUseful).Sum(a => a.Value));
result.Dump(); //Linqpad
result2.Dump(); //Linqpad
I have a enumeration of objects :
public IOrderedEnumerable<RentContract> Contracts {
get { return RentContracts.OrderByDescending(rc => rc.DateCreated); }
}
I have to compare a given RentContract instance with its previous RenContract instance on the list to highlight changes between the two objects, which is the most correct method to get the previous element ?
This is not possible directly. You can do it like this:
var input = new SomeClass[10]; //test data
var zipped = input.Zip(new SomeClass[1].Concat(input), (a, b) => { a, b });
var result = zipped.Where(x => x.b == null || x.a.DateCreated < x.b.DateCreated.AddHours(-1)); //some example
This solution is zipping the sequence with itself, but offset by one null element.