比较两个切片以查看哪个元素已被删除的惯用Go方式

huangapple go评论109阅读模式
英文:

Idiomatic go way to compare 2 slices to see which element has been removed

问题

我在我的应用程序中使用RethinkDB,并且我有一个包含用户的大厅。

RethinkDB具有监视表中更改的能力,当发生更改时,它会自动发出更改,以便您可以对数据进行任何操作。现在我正在尝试实现这样一个功能:当用户离开大厅时,我可以发送一个WebSocket消息来移除该用户。唯一的问题是,我想找出在之前/之后的数据中有什么差异,这是一个members的切片,数据如下:

  1. type change struct {
  2. NewVal *fields `gorethink:"new_val,omitempty"`
  3. OldVal *fields `gorethink:"old_val,omitempty"`
  4. }
  5. type fields struct {
  6. ID string `gorethink:"id"`
  7. Owner string `gorethink:"owner"`
  8. Inqueue bool `gorethink:"inqueue"`
  9. Members []struct {
  10. SteamID string `gorethink:"steamid"`
  11. Username string `gorethink:"username"`
  12. } `gorethink:"members"`
  13. Messages []struct {
  14. Username string `gorethink:"username"`
  15. Message string `gorethink:"message"`
  16. CreatedAt time.Time `gorethink:"createdAt"`
  17. } `gorethink:"messages"`
  18. }

目前我正在使用以下代码:

  1. func (l *lobby) watchChanges() {
  2. db := common.DB()
  3. query := gorethink.Table("Lobbys").Get(l.ID).Changes()
  4. res, err := query.Run(db)
  5. if err != nil {
  6. log.Println(err)
  7. }
  8. go func(res *gorethink.Cursor, l *lobby) {
  9. defer res.Close()
  10. changes := new(change)
  11. for res.Next(&changes) {
  12. if changes.NewVal != nil && changes.OldVal != nil {
  13. switch {
  14. case len(changes.NewVal.Members) > len(changes.OldVal.Members):
  15. // Member has joined so announce who it was.
  16. case len(changes.NewVal.Members) < len(changes.OldVal.Members):
  17. // Member has left so announce who it was.
  18. -------->
  19. case len(changes.NewVal.Messages) > len(changes.OldVal.Messages):
  20. // New Message was received so announce the message.
  21. }
  22. }
  23. }
  24. }(res, l)
  25. select {
  26. case <-l.KillMe:
  27. res.Close()
  28. break
  29. }
  30. }

新增的条目很容易,我只需取出切片的末尾并发送即可。但是当用户离开时,我该如何比较changes.NewVal.Memberschanges.OldVal.Members以查看删除了哪个索引,以便通过WebSocket发送正确的成员进行移除。希望我的问题清楚,如果不清楚,请告诉我。

这是我目前的做法

  1. removedIndex := 0
  2. for i, oldMember := range changes.OldVal.Members {
  3. foundMissing := true
  4. for _, newMember := range changes.NewVal.Members {
  5. if reflect.DeepEqual(oldMember, newMember) {
  6. foundMissing = false
  7. }
  8. }
  9. if foundMissing {
  10. removedIndex = i
  11. break
  12. }
  13. }

但是这种方法感觉有点笨拙,有没有更好的方法?

英文:

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:

  1. type change struct {
  2. NewVal *fields `gorethink:&quot;new_val,omitempty&quot;`
  3. OldVal *fields `gorethink:&quot;old_val,omitempty&quot;`
  4. }
  5. type fields struct {
  6. ID string `gorethink:&quot;id&quot;`
  7. Owner string `gorethink:&quot;owner&quot;`
  8. Inqueue bool `gorethink:&quot;inqueue&quot;`
  9. Members []struct {
  10. SteamID string `gorethink:&quot;steamid&quot;`
  11. Username string `gorethink:&quot;username&quot;`
  12. } `gorethink:&quot;members&quot;`
  13. Messages []struct {
  14. Username string `gorethink:&quot;username&quot;`
  15. Message string `gorethink:&quot;message&quot;`
  16. CreatedAt time.Time `gorethink:&quot;createdAt&quot;`
  17. } `gorethink:&quot;messages&quot;`
  18. }

Right now I'm doing

  1. func (l *lobby) watchChanges() {
  2. db := common.DB()
  3. query := gorethink.Table(&quot;Lobbys&quot;).Get(l.ID).Changes()
  4. res, err := query.Run(db)
  5. if err != nil {
  6. log.Println(err)
  7. }
  8. go func(res *gorethink.Cursor, l *lobby) {
  9. defer res.Close()
  10. changes := new(change)
  11. for res.Next(&amp;changes) {
  12. if changes.NewVal != nil &amp;&amp; changes.OldVal != nil {
  13. switch {
  14. case len(changes.NewVal.Members) &gt; len(changes.OldVal.Members):
  15. // Member has joined so announce who it was.
  16. case len(changes.NewVal.Members) &lt; len(changes.OldVal.Members):
  17. // Member has left so announce who it was.
  18. --------&gt;
  19. case len(changes.NewVal.Messages) &gt; len(changes.OldVal.Messages):
  20. // New Message was recieved so announce the message.
  21. }
  22. }
  23. }
  24. }(res, l)
  25. select {
  26. case &lt;-l.KillMe:
  27. res.Close()
  28. break
  29. }
  30. }

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

  1. removedIndex := 0
  2. for i, oldMember := range changes.OldVal.Members {
  3. foundMissing := true
  4. for _, newMember := range changes.NewVal.Members {
  5. if reflect.DeepEqual(oldMember, newMember) {
  6. foundMissing = false
  7. }
  8. }
  9. if foundMissing {
  10. removedIndex = i
  11. break
  12. }
  13. }

but it feels a bit hacky, is there a better way?

答案1

得分: 1

按照唯一且可排序的键对旧成员和新成员进行排序。看起来 SteamID 可能适合这个目的。通过比较键来迭代两个切片,检查添加和删除的元素。

  1. func diff(old []*member, new []*member) {
  2. sort.Sort(bySteamID(old))
  3. sort.Sort(bySteamID(new))
  4. i, j := 0, 0
  5. for i < len(old) && j < len(new) {
  6. switch {
  7. case old[i].SteamID < new[j].SteamID:
  8. fmt.Println(" delete", old[i].SteamID)
  9. i++
  10. case old[i].SteamID > new[j].SteamID:
  11. fmt.Println(" add", new[j].SteamID)
  12. j++
  13. default:
  14. i++
  15. j++
  16. }
  17. }
  18. for i < len(old) {
  19. fmt.Println(" delete", old[i].SteamID)
  20. i++
  21. }
  22. for j < len(new) {
  23. fmt.Println(" add", new[j].SteamID)
  24. j++
  25. }
  26. }

playground 示例

英文:

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.

  1. func diff(old []*member, new []*member) {
  2. sort.Sort(bySteamID(old))
  3. sort.Sort(bySteamID(new))
  4. i, j := 0, 0
  5. for i &lt; len(old) &amp;&amp; j &lt; len(new) {
  6. switch {
  7. case old[i].SteamID &lt; new[j].SteamID:
  8. fmt.Println(&quot; delete&quot;, old[i].SteamID)
  9. i++
  10. case old[i].SteamID &gt; new[j].SteamID:
  11. fmt.Println(&quot; add&quot;, new[j].SteamID)
  12. j++
  13. default:
  14. i++
  15. j++
  16. }
  17. }
  18. for i &lt; len(old) {
  19. fmt.Println(&quot; delete&quot;, old[i].SteamID)
  20. i++
  21. }
  22. for j &lt; len(new) {
  23. fmt.Println(&quot; add&quot;, new[j].SteamID)
  24. j++
  25. }
  26. }

<kbd>playground example</kbd>

huangapple
  • 本文由 发表于 2016年1月6日 10:35:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/34624701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定