Coder Social home page Coder Social logo

gohobby / deepcopy Goto Github PK

View Code? Open in Web Editor NEW
7.0 1.0 2.0 66 KB

Create deep copies (clones) of your maps and slices without using reflection.

License: MIT License

Makefile 4.62% Go 95.38%
go golang copy deepcopy deepclone clone golang-package golang-library

deepcopy's Introduction

DeepCopy

Go Reference Go Report Card Test GitHub release GitHub license

DeepCopy helps you create deep copies (clones) of your maps and slices.

The package is based on type assertions and does not use reflection.

Installation

Install DeepCopy with the go get command:

go get -u github.com/gohobby/deepcopy

How it works

DeepCopy returns a new object with all recursively duplicated children. This means that changes made to the original object will not affect the copied object and vice versa.

To copy a card or a slice:

m := map[string]interface{}{"foo": []string{"bar", "baz"}}
cloneMap := deepcopy.Map(m).DeepCopy() // interface{}

s := []interface{}{1, 2, &m}
cloneSlice := deepcopy.Slice(s).DeepCopy() // interface{}

You can also use the Clone function to get the copy directly into the expected type, for example:

m := map[string]interface{}{"foo": []string{"bar", "baz"}}
cloneMap := deepcopy.Map(m).CLone() // map[string]interface{}

s := []interface{}{1, 2, &m}
cloneSlice := deepcopy.Slice(s).Clone() // []interface{}

To copy your custom types, implement the Copyable interface and define your own deep copy function:

type Map map[string]interface{}

func (n Map) DeepCopy() interface{} {
	clone := make(Map, len(n))

	for k, v := range n {
		clone[k] = deepcopy.DeepCopy(v)
	}

	return clone
}
Example

package main

import (
	"fmt"

	"github.com/gohobby/deepcopy"
)

type Map map[string]interface{}

func (n Map) DeepCopy() interface{} {
	clone := make(Map, len(n))

	for k, v := range n {
		clone[k] = deepcopy.DeepCopy(v)
	}

	return clone
}

var nestedMap = Map{
	"flag": "๐Ÿ‡ซ๐Ÿ‡ท",
	"country": Map{
		"city": "Paris",
	},
}

func main() {
	// Deep Copy
	deepClone := nestedMap.DeepCopy().(Map)

	// Change of the cloned object
	deepClone["flag"] = "๐Ÿ‡ฎ๐Ÿ‡น"
	deepClone["country"].(Map)["city"] = "Roma"

	fmt.Printf("%#v\n", deepClone)
	// main.Map{"country":main.Map{"city":"Roma"}, "flag":"๐Ÿ‡ฎ๐Ÿ‡น"} <-- โœ…

	fmt.Printf("%#v\n\n", nestedMap)
	// main.Map{"country":main.Map{"city":"Paris"}, "flag":"๐Ÿ‡ซ๐Ÿ‡ท"} <-- โœ…

	fmt.Printf("%p\n", deepClone["country"]) // 0xc000012240
	fmt.Printf("%p\n", nestedMap["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

Why?

Mutability

Map types are pointers which make them mutable objects.

When you write the statement

m := make(map[int]int)

The compiler replaces it with a call to runtime.makemap, which has the signature

// makemap implements Go map creation for make(map[k]v, hint).
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap

As you can see, the type of the value returned by runtime.makemap is a pointer to a runtime.hmap structure.

See Dave Cheney's article for more details.

Example

package main

import "fmt"

func main() {
	obj := map[string]int{"one": 1, "two": 2}

	obj2 := obj

	fmt.Printf("(obj)  %v\n(obj2) %v\n\n",
		obj,  // map[one:1 two:2]
		obj2, // map[one:1 two:2]
	)

	obj2["three"] = 3

	fmt.Printf("(obj2) %v\n", obj2)
	// map[one:1 three:3 two:2] <-- โœ…
	fmt.Printf("(obj)  %v\n", obj)
	// map[one:1 three:3 two:2] <-- โŒ
}

Run this code in GoPlayground

How to create copies of your maps?

Shallow Copy

A shallow copy means that the first level is copied, deeper levels are referenced.

shallowcopy

Example

package main

import "fmt"

var nestedObject = map[string]interface{}{
	"flag": "๐Ÿ‡ซ๐Ÿ‡ท",
	"country": map[string]interface{}{
		"city": "Paris",
	},
}

func main() {
	// Shallow Copy
	shallowClone := make(map[string]interface{}, len(nestedObject))

	for k, v := range nestedObject {
		shallowClone[k] = v
	}

	// Change of the cloned object
	shallowClone["flag"] = "๐Ÿ‡ฎ๐Ÿ‡น"
	shallowClone["country"].(map[string]interface{})["city"] = "Roma"

	fmt.Printf("%v\n", shallowClone)
	// map[country:map[city:Roma] flag:๐Ÿ‡ฎ๐Ÿ‡น] <-- โœ…

	fmt.Printf("%v\n\n", nestedObject)
	// map[country:map[city:Roma] flag:๐Ÿ‡ซ๐Ÿ‡ท] <-- โŒ was mutated

	fmt.Printf("%p\n", shallowClone["country"]) // 0xc0000121e0
	fmt.Printf("%p\n", nestedObject["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

Deep Copy

A deep copy is a shallow copy applied recursively to all sub objects.

deepcopy

Example

package main

import (
	"fmt"

	"github.com/gohobby/deepcopy"
)

var nestedObject = map[string]interface{}{
	"flag": "๐Ÿ‡ซ๐Ÿ‡ท",
	"country": map[string]interface{}{
		"city": "Paris",
	},
}

func main() {
	// Deep Copy
	deepClone := deepcopy.Map(nestedObject).Clone()

	// Change of the cloned object
	deepClone["flag"] = "๐Ÿ‡ฎ๐Ÿ‡น"
	deepClone["country"].(map[string]interface{})["city"] = "Roma"

	fmt.Printf("%v\n", deepClone)
	// map[country:map[city:Roma] flag:๐Ÿ‡ฎ๐Ÿ‡น] <-- โœ…

	fmt.Printf("%v\n\n", nestedObject)
	// map[country:map[city:Paris] flag:๐Ÿ‡ซ๐Ÿ‡ท] <-- โœ…

	fmt.Printf("%p\n", deepClone["country"])    // 0xc000012240
	fmt.Printf("%p\n", nestedObject["country"]) // 0xc0000121e0
}

Run this code in GoPlayground

deepcopy's People

Contributors

dependabot[bot] avatar hgtgh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

shista2010

deepcopy's Issues

Panic for nested map with nil fields

Hello!
It throws a panic for nested map with with nil fields.

package main

import (
	"encoding/json"

	"github.com/gohobby/deepcopy"
)

var msg = `
{
    "one": {
        "two": null
    }
}
`

func main() {
	var payload map[string]any
	json.Unmarshal([]byte(msg), &payload)
	deepcopy.Map(payload).Clone()
}
panic: interface conversion: interface is nil, not interface {}

goroutine 1 [running]:
github.com/gohobby/deepcopy.DeepCopy({0x4ba120?, 0xc000012270?})
	/tmp/gopath900186122/pkg/mod/github.com/gohobby/[email protected]/deepcopy.go:277 +0x4bb7
github.com/gohobby/deepcopy.Map.DeepCopy(0xc0000121b0)
	/tmp/gopath900186122/pkg/mod/github.com/gohobby/[email protected]/deepcopy_map.go:13 +0xc7
github.com/gohobby/deepcopy.Map.Clone(...)
	/tmp/gopath900186122/pkg/mod/github.com/gohobby/[email protected]/deepcopy_map.go:22
main.main()
	/tmp/sandbox2139623867/prog.go:20 +0x5d

Program exited.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.