Skip to content

Commit da46c54

Browse files
Fix Virtualise and Page exception. Fixes #540 (#667)
1 parent 9f7638f commit da46c54

5 files changed

Lines changed: 64 additions & 58 deletions

File tree

src/DynamicData.Tests/List/PageFixture.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reactive.Linq;
45
using System.Reactive.Subjects;
@@ -166,6 +167,24 @@ public void SimplePaging()
166167
pager.OnNext(new PageRequest(2, 3));
167168
sut.Paged.Count.Should().Be(3);
168169
}
170+
171+
172+
[Fact]
173+
public void DoesNotThrowWithDuplicates()
174+
{
175+
// see https://github.com/reactivemarbles/DynamicData/issues/540
176+
177+
var result = new List<string>();
178+
179+
var source = new SourceList<string>();
180+
source.AddRange(Enumerable.Repeat("item", 10));
181+
source.Connect()
182+
.Page(new BehaviorSubject<IPageRequest>(new PageRequest(0, 3)))
183+
.Clone(result)
184+
.Subscribe();
185+
186+
result.Count.Should().Be(1);
187+
}
169188
}
170189

171190
public class SimplePaging : AbstractNotifyPropertyChanged, IDisposable

src/DynamicData.Tests/List/VirtualisationFixture.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Reactive.Subjects;
45

@@ -128,4 +129,22 @@ public void VirtualiseInitial()
128129

129130
_results.Data.Items.Should().BeEquivalentTo(expected);
130131
}
132+
133+
134+
[Fact]
135+
public void DoesNotThrowWithDuplicates()
136+
{
137+
// see https://github.com/reactivemarbles/DynamicData/issues/540
138+
139+
var result = new List<string>();
140+
141+
var source = new SourceList<string>();
142+
source.AddRange(Enumerable.Repeat("item", 10));
143+
source.Connect()
144+
.Virtualise(new BehaviorSubject<IVirtualRequest>(new VirtualRequest(0, 3)))
145+
.Clone(result)
146+
.Subscribe();
147+
148+
result.Count.Should().Be(1);
149+
}
131150
}

src/DynamicData/List/ChangeAwareList.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public IChangeSet<T> CaptureChanges()
147147
if (_changes.Count == 0)
148148
return ChangeSet<T>.Empty;
149149

150-
ChangeSet<T> returnValue = _changes;
150+
var returnValue = _changes;
151151

152152
// we can infer this is a Clear
153153
if (_innerList.Count == 0 && returnValue.Removes == returnValue.TotalChanges && returnValue.TotalChanges > 1)

src/DynamicData/List/Internal/Pager.cs

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
// Roland Pheasant licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for full license information.
44

5-
using System;
65
using System.Collections;
7-
using System.Collections.Generic;
8-
using System.Linq;
96
using System.Reactive.Linq;
107

118
using DynamicData.Kernel;
@@ -95,7 +92,7 @@ private static PageChangeSet<T> Page(List<T> all, ChangeAwareList<T> paged, IPag
9592
int page = request.Page > pages ? pages : request.Page;
9693
int skip = request.Size * (page - 1);
9794

98-
var current = all.Skip(skip)
95+
var current = all.Distinct().Skip(skip)
9996
.Take(request.Size)
10097
.ToList();
10198

@@ -104,39 +101,27 @@ private static PageChangeSet<T> Page(List<T> all, ChangeAwareList<T> paged, IPag
104101

105102
paged.RemoveMany(removes);
106103

107-
adds.ForEach(t =>
104+
foreach (var add in adds)
108105
{
109-
var index = current.IndexOf(t);
110-
paged.Insert(index, t);
111-
});
106+
var index = current.IndexOf(add);
107+
paged.Insert(index, add);
108+
}
112109

113110
var startIndex = skip;
114111

115-
var moves = changeSet.EmptyIfNull()
116-
.Where(change => change.Reason == ListChangeReason.Moved
117-
&& change.MovedWithinRange(startIndex, startIndex + request.Size));
118-
119-
foreach (var change in moves)
120-
{
121-
// check whether an item has moved within the same page
122-
var currentIndex = change.Item.CurrentIndex - startIndex;
123-
var previousIndex = change.Item.PreviousIndex - startIndex;
124-
paged.Move(previousIndex, currentIndex);
125-
}
126-
127-
// find replaces [Is this ever the case that it can be reached]
128-
for (int i = 0; i < current.Count; i++)
112+
if (changeSet is not null && changeSet.Count != 0)
129113
{
130-
var currentItem = current[i];
131-
var previousItem = previous[i];
114+
var moves = changeSet
115+
.Where(change => change.Reason == ListChangeReason.Moved
116+
&& change.MovedWithinRange(startIndex, startIndex + request.Size));
132117

133-
if (ReferenceEquals(currentItem, previousItem))
118+
foreach (var change in moves)
134119
{
135-
continue;
120+
// check whether an item has moved within the same page
121+
var currentIndex = change.Item.CurrentIndex - startIndex;
122+
var previousIndex = change.Item.PreviousIndex - startIndex;
123+
paged.Move(previousIndex, currentIndex);
136124
}
137-
138-
var index = paged.IndexOf(currentItem);
139-
paged.Move(i, index);
140125
}
141126

142127
var changed = paged.CaptureChanges();

src/DynamicData/List/Internal/Virtualiser.cs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
// Roland Pheasant licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for full license information.
44

5-
using System;
6-
using System.Collections.Generic;
7-
using System.Linq;
85
using System.Reactive.Linq;
9-
106
using DynamicData.Kernel;
117

128
namespace DynamicData.List.Internal;
@@ -69,43 +65,30 @@ private static IChangeSet<T> Virtualise(IList<T> all, ChangeAwareList<T> virtual
6965

7066
var previous = virtualised;
7167

72-
var current = all.Skip(request.StartIndex).Take(request.Size).ToList();
68+
var current = all.Distinct().Skip(request.StartIndex).Take(request.Size).ToList();
7369

7470
var adds = current.Except(previous);
7571
var removes = previous.Except(current);
7672

7773
virtualised.RemoveMany(removes);
7874

79-
adds.ForEach(
80-
t =>
81-
{
82-
var index = current.IndexOf(t);
83-
virtualised.Insert(index, t);
84-
});
85-
86-
var moves = changeSet.EmptyIfNull().Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(request.StartIndex, request.StartIndex + request.Size));
87-
88-
foreach (var change in moves)
75+
foreach (var add in adds)
8976
{
90-
// check whether an item has moved within the same page
91-
var currentIndex = change.Item.CurrentIndex - request.StartIndex;
92-
var previousIndex = change.Item.PreviousIndex - request.StartIndex;
93-
virtualised.Move(previousIndex, currentIndex);
77+
var index = current.IndexOf(add);
78+
virtualised.Insert(index, add);
9479
}
9580

96-
// find replaces [Is this ever the case that it can be reached]
97-
for (var i = 0; i < current.Count; i++)
81+
if (changeSet is not null && changeSet.Count != 0)
9882
{
99-
var currentItem = current[i];
100-
var previousItem = previous[i];
83+
var moves = changeSet.EmptyIfNull().Where(change => change.Reason == ListChangeReason.Moved && change.MovedWithinRange(request.StartIndex, request.StartIndex + request.Size));
10184

102-
if (ReferenceEquals(currentItem, previousItem))
85+
foreach (var change in moves)
10386
{
104-
continue;
87+
// check whether an item has moved within the same page
88+
var currentIndex = change.Item.CurrentIndex - request.StartIndex;
89+
var previousIndex = change.Item.PreviousIndex - request.StartIndex;
90+
virtualised.Move(previousIndex, currentIndex);
10591
}
106-
107-
var index = virtualised.IndexOf(currentItem);
108-
virtualised.Move(i, index);
10992
}
11093

11194
return virtualised.CaptureChanges();

0 commit comments

Comments
 (0)