Git Product home page Git Product logo

diffutil.dart's Introduction

Hi there ๐Ÿ‘‹

diffutil.dart's People

Contributors

knaeckekami avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

diffutil.dart's Issues

RangeError (index): Invalid value: Not in inclusive range

Help please. When i try to use a simple solution i get error. Maybe I am using diffUtil incorrectly somehow?...

My code:

    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;
        },
      );
    }

Error i get:

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

What i expected:

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

getUpdatesWithData - change return type from Iterable to List

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.

Nice Idea about DiffUtil

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.

While loop runs forever

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++;

DataChange not dispatched when value in List changes

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?

Insertion order with `getUpdatesWithData`

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 ?

Different result with batched parameter when one list is empty

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.

Move detection doesn't work as I expected

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]),
            });

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.