I have a SplayTreeSet
of the objects ChatRoomListModel
. I'd like to order my set based on the objects DateTime createTime
value.
I'm not sure where I'm going wrong because there's duplicate items being added item.
I later down the line perform a _latestMessages.add(newMessage)
and it's not actually calling the overloads and there's duplicates being added.
I tested by using _latestMessage.contains(newMessageButSameChatRoomId)
, it returns false.
When I perform _latestMessage.toSet()
every duplicate goes away.
How can I get SplayTreeSet
to use my overloading equals?
Thanks!
ObservableSet<ChatRoomListModel> _latestMessages = ObservableSet.splayTreeSetFrom(
ObservableSet(),
compare: (a, b) => b.compareTo(a),
);
The ChatRoomListModel
model has the following methods and overloads:
int compareTo(ChatRoomListModel other){
return messagesModel.createTime.compareTo(other.messagesModel.createTime);
}
ChatRoomListModel copyWith({
String? firstName,
String? otherUserId,
MessagesModel? messagesModel,
}) {
return ChatRoomListModel(
firstName: firstName ?? this.firstName,
otherUserId: otherUserId ?? this.otherUserId,
messagesModel: messagesModel ?? this.messagesModel,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ChatRoomListModel &&
runtimeType == other.runtimeType &&
messagesModel.chatRoomId == other.messagesModel.chatRoomId;
@override
int get hashCode => messagesModel.chatRoomId.hashCode;
Your issue is that you have two completely different notions of what it means for two ChatRoomListModel
objects to be "equal". You provide both compareTo
and operator ==
implementations, but they consider different sets of properties, so compareTo
can return 0 when operator ==
returns false, which is confusing at best. SplayTreeMap
considers only compareTo
, not operator ==
. From the SplayTreeSet
documentation:
Elements of the set are compared using the
compare
function passed in the constructor, both for ordering and for equality. If the set contains only an objecta
, then set.contains(b)
will returntrue
if and only ifcompare(a, b) == 0
, and the value ofa == b
is not even checked.
I'm presuming what you call "duplicates" are elements that have equal chatRoomId
s but that have different creation times, and creation times are the only things that your SplayTreeSet
cares about.
If your goal is to maintain only the latest message per chatRoomId
, you need to maintain a data structure that uses the chatRoomId
(a String
) as a key. The natural collection for that would be a Map<String, ChatRoomListModel>
. (Since the ChatRoomListModel
knows its own chatRoomId
, it also could just be a HashSet
with explicit equals
and hashCode
callbacks.)
If you additionally want to keep messages in chronological order, you either will need to explicitly sort them afterward or maintain a separate data structure that keeps them in chronological order. You could continue using a SplayTreeSet
for that. Basically before you add any entry to the SplayTreeSet
, check the Map
first to see if an existing entry for that chatRoomId
.
I don't fully understand your data structures, but here's an example that you presumably can adapt:
import 'dart:collection';
class Message {
Message(this.creationTime, {required this.chatRoomId, required this.text});
final DateTime creationTime;
final String chatRoomId;
final String text;
@override
String toString() => '$creationTime: [$chatRoomId] $text';
}
class Messages {
final _latestMessages = <String, Message>{};
final _orderedMessages = SplayTreeSet<Message>((message1, message2) {
var result = message1.creationTime.compareTo(message2.creationTime);
if (result != 0) {
return result;
}
result = message1.chatRoomId.compareTo(message2.chatRoomId);
if (result != 0) {
return result;
}
return message1.text.compareTo(message2.text);
});
void add(Message message) {
var existingMessage = _latestMessages[message.chatRoomId];
if (existingMessage != null &&
message.creationTime.compareTo(existingMessage.creationTime) < 0) {
// An existing message exists with a later creation date. Ignore the
// incoming message.
return;
}
_latestMessages[message.chatRoomId] = message;
_orderedMessages.remove(existingMessage);
_orderedMessages.add(message);
}
void printAll() => _orderedMessages.forEach(print);
}
void main() {
var messages = Messages();
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'foo',
text: 'Hello foo!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'bar',
text: 'Goodbye bar!',
));
messages.add(Message(
DateTime(2023, 1, 2),
chatRoomId: 'foo',
text: 'Goodbye foo!',
));
messages.add(Message(
DateTime(2023, 1, 1),
chatRoomId: 'bar',
text: 'Hello bar!',
));
messages.printAll();
}