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

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

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.Memberschanges.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:&quot;new_val,omitempty&quot;`
	OldVal *fields `gorethink:&quot;old_val,omitempty&quot;`
}

type fields struct {
	ID      string `gorethink:&quot;id&quot;`
	Owner   string `gorethink:&quot;owner&quot;`
	Inqueue bool   `gorethink:&quot;inqueue&quot;`
	Members []struct {
		SteamID  string `gorethink:&quot;steamid&quot;`
		Username string `gorethink:&quot;username&quot;`
	} `gorethink:&quot;members&quot;`
	Messages []struct {
		Username  string    `gorethink:&quot;username&quot;`
		Message   string    `gorethink:&quot;message&quot;`
		CreatedAt time.Time `gorethink:&quot;createdAt&quot;`
	} `gorethink:&quot;messages&quot;`
}

Right now I'm doing

func (l *lobby) watchChanges() {
	db := common.DB()
	query := gorethink.Table(&quot;Lobbys&quot;).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(&amp;changes) {
			if changes.NewVal != nil &amp;&amp; changes.OldVal != nil {
				switch {
				case len(changes.NewVal.Members) &gt; len(changes.OldVal.Members):
					// Member has joined so announce who it was.
					
				case len(changes.NewVal.Members) &lt; len(changes.OldVal.Members):
					// Member has left so announce who it was.
           --------&gt;
				case len(changes.NewVal.Messages) &gt; len(changes.OldVal.Messages):
					// New Message was recieved so announce the message.

				}
			}
		}
	}(res, l)

	select {
	case &lt;-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++
  }
}

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.

func diff(old []*member, new []*member) {
  sort.Sort(bySteamID(old))
  sort.Sort(bySteamID(new))
  i, j := 0, 0
  for i &lt; len(old) &amp;&amp; j &lt; len(new) {
	switch {
	case old[i].SteamID &lt; new[j].SteamID:
		fmt.Println(&quot; delete&quot;, old[i].SteamID)
		i++
	case old[i].SteamID &gt; new[j].SteamID:
		fmt.Println(&quot; add&quot;, new[j].SteamID)
		j++
	default:
		i++
		j++
	}
  }
  for i &lt; len(old) {
	fmt.Println(&quot; delete&quot;, old[i].SteamID)
	i++
  }
  for j &lt; len(new) {
	fmt.Println(&quot; add&quot;, new[j].SteamID)
	j++
  }
}

<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:

确定