在goroutine通道修改切片后,如何获取切片的新索引?

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

How can I get a slices new index after a goroutine channel has mutated the slice?

问题

我想知道如何在切片中获取元素的新索引。我有一个从数据库中获取应用程序的函数,并查询特定的应用程序(用于过滤),但当我查询到不需要的应用程序时,我尝试从切片中删除它们,以便只有需要的应用程序显示在视图中。这个功能是有效的,但是我在goroutine中的索引出现了问题,它们是旧的索引,所以当它尝试从切片中删除元素时会出现错误。无论如何,这是我的代码:

// ListApplications将列出应用程序
func ListApplications(w http.ResponseWriter, r *http.Request) {
session := common.Sesh(r)
urlParse, err := url.ParseRequestURI(r.RequestURI)
vals := urlParse.Query()

if err != nil {
    log.Println(err)
}
if lvl, ok := session.Values["user_level"]; !ok || lvl.(int) < 3 {
    http.Redirect(w, r, "/", 401)
    return
}

db := common.Db()

type UserResp struct {
    Action string
    I      int
    User   common.User
}
apps := []common.Application{}
resp := make(chan UserResp)
db.Order("accepted asc").Order("created_at asc").Find(&apps)
var wg sync.WaitGroup
for i, app := range apps {
    wg.Add(1)
    go func(i int, app common.Application) {
        user := common.User{}
        if vals.Get("class") != "" {
            db.Preload("Characters", "character_class = ?", vals.Get("class")).Model(&app).Related(&user)
            if len(user.Characters) == 0 {
                resp <- UserResp{"remove", i, user}
            } else {
                resp <- UserResp{"add", i, user}
            }
            wg.Done()
            return
        }

        db.Preload("Characters").Model(&app).Related(&user)
        log.Println(user)
        resp <- UserResp{"add", i, user}
        wg.Done()
    }(i, app)
}

go func() {
    wg.Wait()
    close(resp)
}()

for user := range resp {
    switch user.Action {
    case "add":
        apps[user.I].User = user.User
    case "remove":
        log.Println(len(apps), user.I)
        apps = append(apps[:user.I], apps[user.I+1:]...)
    }
}

common.View(w, r, "/application/list", &struct {
    Title string
    Apps  []common.Application
}{"Viewing Applications", apps})

}

我尝试在for user := range resp中的"remove"操作下删除元素。但正如我所说的,通过通道传递的索引有时是过时的,那么我该如何在goroutine中保持持久的索引呢?

英文:

I'm wondering how I can get an elements new index in a slice, I have a function that gets applications from a database, and queries for certain ones (for filtering) but when I query for ones and I get ones I don't need, I'm trying to remove them from the slice so that only the wanted ones make it to the view. It's working but I'm having a problem with the index's in the goroutines are the older index's so when it trys to remove the element from the slice it panics: Anyways here is my code:

// ListApplications will list the applications
func ListApplications(w http.ResponseWriter, r *http.Request) {
	session := common.Sesh(r)
	urlParse, err := url.ParseRequestURI(r.RequestURI)
	vals := urlParse.Query()

	if err != nil {
		log.Println(err)
	}
	if lvl, ok := session.Values[&quot;user_level&quot;]; !ok || lvl.(int) &lt; 3 {
		http.Redirect(w, r, &quot;/&quot;, 401)
		return
	}

	db := common.Db()

	type UserResp struct {
		Action string
		I      int
		User   common.User
	}
	apps := []common.Application{}
	resp := make(chan UserResp)
	db.Order(&quot;accepted asc&quot;).Order(&quot;created_at asc&quot;).Find(&amp;apps)
	var wg sync.WaitGroup
	for i, app := range apps {
		wg.Add(1)
		go func(i int, app common.Application) {
			user := common.User{}
			if vals.Get(&quot;class&quot;) != &quot;&quot; {
				db.Preload(&quot;Characters&quot;, &quot;character_class = ?&quot;, vals.Get(&quot;class&quot;)).Model(&amp;app).Related(&amp;user)
				if len(user.Characters) == 0 {
					resp &lt;- UserResp{&quot;remove&quot;, i, user}
				} else {
					resp &lt;- UserResp{&quot;add&quot;, i, user}
				}
				wg.Done()
				return
			}

			db.Preload(&quot;Characters&quot;).Model(&amp;app).Related(&amp;user)
			log.Println(user)
			resp &lt;- UserResp{&quot;add&quot;, i, user}
			wg.Done()
		}(i, app)
	}

	go func() {
		wg.Wait()
		close(resp)
	}()

	for user := range resp {
		switch user.Action {
		case &quot;add&quot;:
			apps[user.I].User = user.User
		case &quot;remove&quot;:
		log.Println(len(apps), user.I)
			apps = append(apps[:user.I], apps[user.I+1:]...)
		}
	}

	common.View(w, r, &quot;/application/list&quot;, &amp;struct {
		Title string
		Apps  []common.Application
	}{&quot;Viewing Applications&quot;, apps})
}

The for user := range resp is where I'm trying to delete under the &quot;remove&quot; action. But like I said, the index getting passed through on the channel is out dated sometimes so how can I maintain a persistent index in my goroutines

答案1

得分: 2

如果你想从切片中删除元素,请不要使用range循环。

相反,可以通过索引循环遍历切片:

// 从切片中删除2。
slice := []int{1,2,3}
for i := 0; i < len(slice); i++ {
    if slice[i] == 2 {
        slice = append(slice[:i], slice[i+1:]...)
        i--
    }
}
fmt.Println(slice) // [1 3]

关键是在删除当前元素后,将索引光标向后移动一位(以防止向前移动)。

英文:

Don't range over a slice if you want to remove elements from it.

Instead, loop over the slice by index:

// Removes 2 from slice.
slice := []int{1,2,3}
for i := 0; i &lt; len(slice); i++ {
	if slice[i] == 2 {
		slice = append(slice[:i], slice[i+1:]...)
		i--
	}
}
fmt.Println(slice) // [1 3]

The point is bringing the index cursor back by one (so it won't go forward) after you've removed the current element.

huangapple
  • 本文由 发表于 2015年12月17日 07:33:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/34324171.html
匿名

发表评论

匿名网友

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

确定