如何修改切片中结构的字段? 通过其索引引用集合元素将指针保留在切片中

如何解决如何修改切片中结构的字段? 通过其索引引用集合元素将指针保留在切片中

我有一个名为test.json的JSON文件,其中包含:

[
    {
        "name" : "john","interests" : ["hockey","jockey"]
    },{
        "name" : "lima","interests" : ["eating","poker"]
    }
]

现在,我编写了一个golang脚本,该脚本将JSON文件读取到一个结构片段,然后在条件检查后,通过遍历该片段来修改结构字段。

这是我到目前为止尝试过的:

package main

import (
    "log"
    "strings"
    "io/ioutil"
    "encoding/json"
)

type subDB struct {
    Name       string   `json:"name"`
    Interests  []string `json:"interests"`
}

var dbUpdate []subDB

func getJSON() {
    // open the file
    filename := "test.json"
    val,err := ioutil.ReadFile(filename)
    if err != nil {
        log.Fatal(err)
    }
    err = json.Unmarshal(val,&dbUpdate)
}

func (v *subDB) Change(newresponse []string) {
    v.Interests = newresponse
}

func updater(name string,newinterest string) {
    // iterating over the slice of structs
    for _,item := range dbUpdate {
        // checking if name supplied matches to the current struct
        if strings.Contains(item.Name,name) {
            flag := false  // declare a flag variable
            // item.Interests is a slice,so we iterate over it
            for _,intr := range item.Interests {
                // check if newinterest is within any one of slice value
                if strings.Contains(intr,newinterest) {
                    flag = true
                    break  // if we find one,we terminate the loop
                }
            }
            // if flag is false,then we change the Interests field
            // of the current struct
            if !flag {
                // Interests holds a slice of strings
                item.Change([]string{newinterest}) // passing a slice of string
            }
        }
    }
}

func main() {
    getJSON()
    updater("lima","jogging")
    log.Printf("%+v\n",dbUpdate)
}

我得到的输出是:

[{Name:john Interests:[hockey jockey]} {Name:lima Interests:[eating poker]}]

但是我应该得到如下输出:

[{Name:john Interests:[hockey jockey]} {Name:lima Interests:[jogging]}]

我不太确定自己在这里做错了什么,因此如果有人向我指出相同的内容,我将非常负责。我的理解是,由于Change()传递了一个指针,因此应该直接修改该字段。

解决方法

问题

让我们列举一下语言规范says on the for ... range loops

带有“ range”子句的“ for”语句遍历所有条目 数组,切片,字符串或映射的形式,或通道上接收的值。 对于每个条目,它将 iteration值分配给相应的 iteration 变量(如果存在),然后执行该块。

所以,在

for _,item := range dbUpdate { ... }

整个语句构成一个范围,在该范围中声明了名为item变量,并为该变量分配了{em> dbUpdate每个元素的值依次形成第一个到最后一个-语句执行其迭代。

Go中的所有赋值,无论何时何地,都会将要赋值的表达式的值复制到接收该值的变量中。

所以,当你拥有

type subDB struct {
    Name       string   `json:"name"`
    Interests  []string `json:"interests"`
}

var dbUpdate []subDB

您有一个切片,其切片的支持数组包含一组元素,每个元素的类型均为subDB
因此,当for ... range遍历您的切片时,在每次迭代中,将对当前切片元素中包含的subDB值的字段进行浅表复制:将这些字段的值复制到变量{ {1}}。

我们可以这样重写一下:

item

如您所见,如果在循环的主体中对for i := 0; i < len(dbUpdate); i++ { var item subDB item = dbUpdate[i] ... } 进行了突变,则对其所做的更改不会以任何方式影响当前正在迭代的集合的元素。

解决方案

广义上讲,解决方案是完全了解Go在它实现的大多数内容中都非常简单的事实,因此item并不是魔术:迭代变量只是一个变量,并且分配只是一个分配。

关于解决特定问题,有多种方法。

通过其索引引用集合元素

range

一个推论是,有时,当元素很复杂或者您想将其变异委托给某个函数时,您可以使用指向它的指针:

for i := range dbUpdate {
  dbUpdate[i].FieldName = value
}

将指针保留在切片中

如果切片像#p>

for i := range dbUpdate {
  p := &dbUpdate[i]

  mutateSubDB(p)
}

...

func mutateSubDB(p *subDB) {
  p.SomeField = someValue
}

...并且您将保留指向(通常是堆分配的)var dbUpdates []*subDB 值的指针,

SubDB

由于切片包含指针,因此语句自然会将指向SubDB(匿名)变量的指针复制到for _,ptr := range dbUpdate { ... } 中,因此赋值将复制指针。

由于所有包含相同地址的指针都指向相同的值,因此通过保留在迭代变量中的指针来使目标变量发生变异将使切片元素所指向的事物也发生变异。

选择哪种方法通常应取决于考虑因素 ,而不是考虑如何对元素进行迭代-仅仅因为一旦理解了代码为什么不起作用,就不再有此问题了

通常:如果您的值确实很大,请考虑保留指向它们的指针。 如果需要同时从多个位置引用值,请保留指向它们的指针。在其他情况下,直接保留值-可以大大改善CPU数据cache locality(简而言之,到您要访问下一个元素时,其内容很可能已经从内存中获取了,而实际上并没有当CPU必须追逐一个指针以通过它访问某个任意内存位置时发生。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-