knaeckekami / diffutil.dart Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
Help please. When i try to use a simple solution i get error. Maybe I am using diffUtil incorrectly somehow?...
final List<int> oldList = [10, 20,30];
final List<int> newList = [30,40];
final diffResult = calculateListDiff(
oldList,
newList,
);
for (var update in diffResult.getUpdates(batch: false)) {
update.when(
insert: (position, count) {
print("inserted count:$count on pos $position. Its in newList: ${newList[position]}");
return;
},
remove: (position, count) {
print("removed on pos $position. Its in oldList: ${oldList[position]}");
return;
},
change: (position, payload) {
return;
},
move: (from, to) {
print("move in oldList element $from to $to");
return;
},
);
}
dart:core List.[]
test\widget_test.dart 141:81 main.<fn>.<fn>
package:diffutil_dart/src/model/diffupdate.dart 59:18 Insert.when
test\widget_test.dart 138:14 main.<fn>
RangeError (index): Invalid value: Not in inclusive range 0..1: 3
inserted count: 1 on pos 4 Its in oldList: 40
removed on pos 0. Its in oldList: 10
removed on pos 1. Its in oldList: 20
Thanks for this really handy library!
I wonder if there is any reason getUpdatesWithData
returns the changes in reverse order? At least I didn't expect that maybe you could add a little hint in the documentation.
Since I need the values in their "original" order I'm calling diffResult.getUpdatesWithData().toList().reversed
. However I've noticed that internally you are already constructing a List
so I wonder why you decided to return an Iterable
instead. I guess returning an Iterable
would make sense if the construction was lazy but in this case returning the already existing List
seems more appropriate.
Hi there,
I'm also inspired by Android's DiffUtil class. And I want to simply use the specific data during the modification like you mentioned in your README.md.
So I want to use kotlin's verion of DiffUtil. Can you provide a kotlin's version if you have any time? Or can you just tell me how to convert this dart code to kotlin?
Thanks.
int i = 0;
while (i < postponedUpdates.length) {
final update = postponedUpdates.elementAt(i);
if (update.posInOwnerList == posInList && update.removal == removal) {
postponedUpdate = update;
postponedUpdates.removeAt(i);
break;
}
i++;
}
while (i < postponedUpdates.length) {
// re-offset all others
final update = postponedUpdates.elementAt(i);
if (removal) {
update.currentPos--;
} else {
update.currentPos++;
}
}
return postponedUpdate;
I encountered a case where the while loop ran forever if i = 2 and postponedUpdates.length = 3.
I think you miss i++;
Hi, I'm trying to understanding the change
callback on the DataDiffUpdate
class.
Why does
diffutil.calculateListDiff([1, 2, 3], [1, 0, 3]).getUpdatesWithData();
return
[
DataRemove(position: 1, data: 2),
DataInsert(position: 1, data: 0)
];
I would have assumed a single DataChange
.
Can you please explain when a DataChange
will be created?
I'm using you library with an animated list and I'm also aiming to animate the initial items rendering.
I noticed that when using getUpdatesWithData
and comparing an initial empty list with some incoming data, e.g.: []
with [Model1(), Model2()]
, the insertion order will be: Model2
at 0
, Model1
at 0
.
This is obviously bad in terms of UI, since the logic way of seeing items appearing on the screen would start with first and end with the last one.
What's your take on this and have you planned on addressing this @knaeckeKami ?
When performing diff between two lists where the compared list is empty results with different results depending on the batched parameter. To simplify the problem I've created a sample code so that it can be easily tested.
import 'package:diffutil_dart/diffutil.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test("empty -> [...6 items] should have 6 inserts operations with consecutive positions and count 1 when not batched", () {
final apiList = [
SimpleClass("1", "Content 1"),
SimpleClass("2", "Content 2"),
SimpleClass("3", "Content 3"),
SimpleClass("4", "Content 4"),
SimpleClass("5", "Content 5"),
SimpleClass("6", "Content 6"),
];
final dbList = <SimpleClass>[];
final diff = calculateDiff(
SimpleClassDiffDelegate(dbList, apiList),
detectMoves: true,
).getUpdates(batch: false).toList();
expect(diff, const [
Insert(position: 0, count: 1),
Insert(position: 1, count: 1),
Insert(position: 2, count: 1),
Insert(position: 3, count: 1),
Insert(position: 4, count: 1),
Insert(position: 5, count: 1),
]);
});
test("empty -> [...6 items] should have 1 insert operation with count 6 on position 0 when batched", () {
final apiList = [
SimpleClass("1", "Content 1"),
SimpleClass("2", "Content 2"),
SimpleClass("3", "Content 3"),
SimpleClass("4", "Content 4"),
SimpleClass("5", "Content 5"),
SimpleClass("6", "Content 6"),
];
final dbList = <SimpleClass>[];
final diff = calculateDiff(
SimpleClassDiffDelegate(dbList, apiList),
detectMoves: true,
).getUpdates(batch: true).toList();
expect(diff, const [
Insert(position: 0, count: 6),
]);
});
test("[...6 items] -> empty list should have 6 remove operations with 6 consecutive positions and count 1 when not batched", () {
final apiList = <SimpleClass>[];
final dbList = [
SimpleClass("1", "Content 1"),
SimpleClass("2", "Content 2"),
SimpleClass("3", "Content 3"),
SimpleClass("4", "Content 4"),
SimpleClass("5", "Content 5"),
SimpleClass("6", "Content 6"),
];
final diff = calculateDiff(
SimpleClassDiffDelegate(dbList, apiList),
detectMoves: true,
).getUpdates(batch: false).toList();
expect(diff, const [
Remove(position: 5, count: 1),
Remove(position: 4, count: 1),
Remove(position: 3, count: 1),
Remove(position: 2, count: 1),
Remove(position: 1, count: 1),
Remove(position: 0, count: 1),
]);
});
test("[...6 items] -> empty list should have 1 remove operation with position 0 and count 6 when batched", () {
final apiList = <SimpleClass>[];
final dbList = [
SimpleClass("1", "Content 1"),
SimpleClass("2", "Content 2"),
SimpleClass("3", "Content 3"),
SimpleClass("4", "Content 4"),
SimpleClass("5", "Content 5"),
SimpleClass("6", "Content 6"),
];
final diff = calculateDiff(
SimpleClassDiffDelegate(dbList, apiList),
detectMoves: true,
).getUpdates(batch: true).toList();
expect(diff, const [
Remove(position: 0, count: 6),
]);
});
}
class SimpleClass {
final String id;
final String content;
SimpleClass(this.id, this.content);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SimpleClass &&
runtimeType == other.runtimeType &&
id == other.id &&
content == other.content;
@override
int get hashCode => id.hashCode ^ content.hashCode;
}
class SimpleClassDiffDelegate implements DiffDelegate {
final List<SimpleClass> _oldList;
final List<SimpleClass> _newList;
SimpleClassDiffDelegate(this._oldList, this._newList);
@override
bool areContentsTheSame(int oldItemPosition, int newItemPosition) => _oldList[oldItemPosition] == _newList[newItemPosition];
@override
bool areItemsTheSame(int oldItemPosition, int newItemPosition) => _oldList[oldItemPosition].id == _newList[newItemPosition].id;
@override
Object getChangePayload(int oldItemPosition, int newItemPosition) => _newList[newItemPosition];
@override
int getNewListSize() => _newList.length;
@override
int getOldListSize() => _oldList.length;
}
In the first test the result is:
Expected: [
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 1, count: 1},
Insert:Insert{position: 2, count: 1},
Insert:Insert{position: 3, count: 1},
Insert:Insert{position: 4, count: 1},
Insert:Insert{position: 5, count: 1}
]
Actual: [
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 0, count: 1},
Insert:Insert{position: 0, count: 1}
]
Which: at location [1] is Insert:<Insert{position: 0, count: 1}> instead of Insert:<Insert{position: 1, count: 1}>
I think that we should obtain the result as in the test case, so that we can easily create a list of all items added.
expect(
calculateListDiff([1, 0, 2, 0, 3], [1, 0, 3], detectMoves: false).getUpdates().toList(),
[const Remove(position: 2, count: 2)],
);
Expected: [Remove:Remove{position: 2, count: 2}]
Actual: [Remove:Remove{position: 3, count: 1}, Remove:Remove{position: 1, count: 1}]
The first test case in the following snippet works correctly but not the second one. Am I doing something wrong?
import 'package:collection/collection.dart';
import 'package:diffutil_dart/diffutil.dart';
main() {
final firstCaseInit = [1, 2, 3];
final firstCaseExpected = [3, 2, 1];
final secondCaseInit = [1, 2, 3, 4, 5, 6];
final secondCaseExpected = [1, 4, 2, 5, 6, 3];
final firstCaseDiffs =
calculateListDiff(firstCaseInit, firstCaseExpected, detectMoves: true)
.getUpdatesWithData();
print(firstCaseDiffs);
// prints [Move{from: 1, to: 2, data: 2}, Move{from: 0, to: 2, data: 1}]
// this is good
final secondCaseDiffs =
calculateListDiff(secondCaseInit, secondCaseExpected, detectMoves: true)
.getUpdatesWithData();
print(secondCaseDiffs);
// prints [Move{from: 2, to: 5, data: 3}, Move{from: 1, to: 3, data: 2}]
// this doesn't make sense
final firstCaseResult = applyDiffs(firstCaseInit, firstCaseDiffs);
final secondCaseResult = applyDiffs(secondCaseInit, secondCaseDiffs);
print(firstCaseResult.equals(firstCaseExpected)); // prints true
print(secondCaseResult.equals(secondCaseExpected)); // prints false, the result is [1 ,4 ,5 ,2 ,6 ,3]
}
List<T> applyDiffs<T>(List<T> init, Iterable<DataDiffUpdate<T>> diffs) =>
diffs.fold(
init,
(state, diff) => switch (diff) {
DataInsert(:final position, :final data) => state.toList()
..insert(position, data),
DataRemove(:final position) => state.toList()..removeAt(position),
DataMove(:final from, :final to, :final data) => state.toList()
..removeAt(from)
..insert(to, data),
DataChange(:final position, :final newData) => state.toList()
..replaceRange(position, position + 1, [newData]),
});
How to perform intensive calculations for diff util in an isolate ? @knaeckeKami
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.