英文:
Idiomatic go way to compare 2 slices to see which element has been removed
问题
我在我的应用程序中使用RethinkDB,并且我有一个包含用户的大厅。
RethinkDB具有监视表中更改的能力,当发生更改时,它会自动发出更改,以便您可以对数据进行任何操作。现在我正在尝试实现这样一个功能:当用户离开大厅时,我可以发送一个WebSocket消息来移除该用户。唯一的问题是,我想找出在之前/之后的数据中有什么差异,这是一个members
的切片,数据如下:
type change struct {
NewVal *fields `gorethink:"new_val,omitempty"`
OldVal *fields `gorethink:"old_val,omitempty"`
}
type fields struct {
ID string `gorethink:"id"`
Owner string `gorethink:"owner"`
Inqueue bool `gorethink:"inqueue"`
Members []struct {
SteamID string `gorethink:"steamid"`
Username string `gorethink:"username"`
} `gorethink:"members"`
Messages []struct {
Username string `gorethink:"username"`
Message string `gorethink:"message"`
CreatedAt time.Time `gorethink:"createdAt"`
} `gorethink:"messages"`
}
目前我正在使用以下代码:
func (l *lobby) watchChanges() {
db := common.DB()
query := gorethink.Table("Lobbys").Get(l.ID).Changes()
res, err := query.Run(db)
if err != nil {
log.Println(err)
}
go func(res *gorethink.Cursor, l *lobby) {
defer res.Close()
changes := new(change)
for res.Next(&changes) {
if changes.NewVal != nil && changes.OldVal != nil {
switch {
case len(changes.NewVal.Members) > len(changes.OldVal.Members):
// Member has joined so announce who it was.
case len(changes.NewVal.Members) < len(changes.OldVal.Members):
// Member has left so announce who it was.
-------->
case len(changes.NewVal.Messages) > len(changes.OldVal.Messages):
// New Message was received so announce the message.
}
}
}
}(res, l)
select {
case <-l.KillMe:
res.Close()
break
}
}
新增的条目很容易,我只需取出切片的末尾并发送即可。但是当用户离开时,我该如何比较changes.NewVal.Members
和changes.OldVal.Members
以查看删除了哪个索引,以便通过WebSocket发送正确的成员进行移除。希望我的问题清楚,如果不清楚,请告诉我。
这是我目前的做法
removedIndex := 0
for i, oldMember := range changes.OldVal.Members {
foundMissing := true
for _, newMember := range changes.NewVal.Members {
if reflect.DeepEqual(oldMember, newMember) {
foundMissing = false
}
}
if foundMissing {
removedIndex = i
break
}
}
但是这种方法感觉有点笨拙,有没有更好的方法?
英文:
I am using RethinkDB in my application and I have a lobby that has users.
RethinkDB has the ability to watch for changes to a table and when changes happen it automagically emits the changes so you can do whatever you want with the data, right now I'm trying to make it so when a user leaves the lobby I can send a websocket out to remove the user. The only thing is I'm trying to find out where the difference is in the before / after data which is a slice of members
, this is the data:
type change struct {
NewVal *fields `gorethink:"new_val,omitempty"`
OldVal *fields `gorethink:"old_val,omitempty"`
}
type fields struct {
ID string `gorethink:"id"`
Owner string `gorethink:"owner"`
Inqueue bool `gorethink:"inqueue"`
Members []struct {
SteamID string `gorethink:"steamid"`
Username string `gorethink:"username"`
} `gorethink:"members"`
Messages []struct {
Username string `gorethink:"username"`
Message string `gorethink:"message"`
CreatedAt time.Time `gorethink:"createdAt"`
} `gorethink:"messages"`
}
Right now I'm doing
func (l *lobby) watchChanges() {
db := common.DB()
query := gorethink.Table("Lobbys").Get(l.ID).Changes()
res, err := query.Run(db)
if err != nil {
log.Println(err)
}
go func(res *gorethink.Cursor, l *lobby) {
defer res.Close()
changes := new(change)
for res.Next(&changes) {
if changes.NewVal != nil && changes.OldVal != nil {
switch {
case len(changes.NewVal.Members) > len(changes.OldVal.Members):
// Member has joined so announce who it was.
case len(changes.NewVal.Members) < len(changes.OldVal.Members):
// Member has left so announce who it was.
-------->
case len(changes.NewVal.Messages) > len(changes.OldVal.Messages):
// New Message was recieved so announce the message.
}
}
}
}(res, l)
select {
case <-l.KillMe:
res.Close()
break
}
}
The new entries are easy enough I'll just take the end off the slice and send that, but when it comes to the user leaving, how can I compare the changes.NewVal.Members
and changes.OldVal.Members
to see which index was removed so I can send the right member to remove through the websocket. Hope my question is clear let me know if it isn't.
This is how I'm currently doing it
removedIndex := 0
for i, oldMember := range changes.OldVal.Members {
foundMissing := true
for _, newMember := range changes.NewVal.Members {
if reflect.DeepEqual(oldMember, newMember) {
foundMissing = false
}
}
if foundMissing {
removedIndex = i
break
}
}
but it feels a bit hacky, is there a better way?
答案1
得分: 1
按照唯一且可排序的键对旧成员和新成员进行排序。看起来 SteamID 可能适合这个目的。通过比较键来迭代两个切片,检查添加和删除的元素。
func diff(old []*member, new []*member) {
sort.Sort(bySteamID(old))
sort.Sort(bySteamID(new))
i, j := 0, 0
for i < len(old) && j < len(new) {
switch {
case old[i].SteamID < new[j].SteamID:
fmt.Println(" delete", old[i].SteamID)
i++
case old[i].SteamID > new[j].SteamID:
fmt.Println(" add", new[j].SteamID)
j++
default:
i++
j++
}
}
for i < len(old) {
fmt.Println(" delete", old[i].SteamID)
i++
}
for j < len(new) {
fmt.Println(" add", new[j].SteamID)
j++
}
}
英文:
Sort the old and new members by a unique and sortable key. It looks like SteamID might be suitable for this purpose. Iterate through both slices checking for added and deleted elements by comparing the keys.
func diff(old []*member, new []*member) {
sort.Sort(bySteamID(old))
sort.Sort(bySteamID(new))
i, j := 0, 0
for i < len(old) && j < len(new) {
switch {
case old[i].SteamID < new[j].SteamID:
fmt.Println(" delete", old[i].SteamID)
i++
case old[i].SteamID > new[j].SteamID:
fmt.Println(" add", new[j].SteamID)
j++
default:
i++
j++
}
}
for i < len(old) {
fmt.Println(" delete", old[i].SteamID)
i++
}
for j < len(new) {
fmt.Println(" add", new[j].SteamID)
j++
}
}
<kbd>playground example</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论