Wie kopiere ich eine Karte?

72

Ich versuche, den Inhalt einer map ( amap) in eine andere ( aSuperMap) zu kopieren und dann zu löschen, amapdamit sie bei der nächsten Iteration / Schleife neue Werte annehmen kann. Das Problem ist, dass Sie die Karte nicht löschen können, ohne ihre Referenz auch in der Supermap zu löschen . Hier ist ein Pseudocode.

for something := range fruits{
        aMap := make(map[string]aStruct)
        aSuperMap := make(map[string]map[string]aStruct)

        for x := range something{
            aMap[x] = aData
            aSuperMap[y] = aMap
            delete(aMap, x)
    }
//save aSuperMap
  saveASuperMap(something)

}

Ich habe auch einige dynamische Sachen ausprobiert, aber offensichtlich gibt es einen Fehler (kann nicht auf Null zugewiesen werden)

aSuperMap[y][x] = aData

Die Frage ist, wie ich eine assoziative Karte erstellen kann. In PHP verwende ich einfach aSuperMap [y] [x] = aData. Es scheint, dass Golang keine offensichtliche Methode hat. Wenn ich delete(aMap, x)seinen Verweis aus der Superkarte lösche, wird er ebenfalls gelöscht. Wenn ich es nicht lösche, erhält die Supermap doppelte Daten. Grundsätzlich erhält es in jeder Schleife aMapden neuen Wert plus alle alten Werte.

Der Benutzer ohne Hut
quelle

Antworten:

121

Sie kopieren nicht die Karte, sondern den Verweis auf die Karte. Sie deleteändern somit die Werte sowohl in Ihrer Originalkarte als auch in der Superkarte. Um eine Karte zu kopieren, müssen Sie eine forSchleife wie die folgende verwenden:

for k,v := range originalMap {
  newMap[k] = v
}

Hier ist ein Beispiel aus der SO-Dokumentation im Ruhestand:

// Create the original map
originalMap := make(map[string]int)
originalMap["one"] = 1
originalMap["two"] = 2

// Create the target map
targetMap := make(map[string]int)

// Copy from the original map to the target map
for key, value := range originalMap {
  targetMap[key] = value
}

Auszug aus Karten - Kopieren Sie eine Karte . Der ursprüngliche Autor war JepZ . Zuordnungsdetails finden Sie auf der Beitragsseite . Die Quelle ist unter CC BY-SA 3.0 lizenziert und befindet sich im Dokumentationsarchiv . Referenzthema ID: 732 und Beispiel-ID: 9834.

Christian
quelle
4
Siehe auch http://golang.org/doc/effective_go.html#maps . Der wichtige Teil ist wirklich der "Verweis auf die zugrunde liegende Datenstruktur". Dies gilt auch für Scheiben.
Seong
1
Vielen Dank! Ich wollte das (für Schleife) verwenden, aber ich hatte Angst, dass es irgendwie übertrieben ist und denke das immer noch.
Der Benutzer ohne Hut
Trotzdem scheint es, als würde ich mich im Kreis bewegen. Ich denke, die neue Karte newMap [k] wird überladen, so dass ich sie in der nächsten Schleife nicht wirklich leeren kann, ohne auch die Werte aus der Superkarte zu verlieren.
Der Benutzer ohne Hut
Wenn originalMap die Map [Map [String] -Schnittstelle {}] ist, deren Elemente dieselbe JSON-Struktur haben, werden bei der nachfolgenden Iteration die ursprünglichen Werte in newMap [0] durch Schlüssel: Wert-Paare von originalMap [1] überschrieben. Außerdem wird newMap [1] mit Schlüssel: Wert-Paaren von originalMap [1] erstellt. Irgendeine Erklärung für dieses Verhalten? Wie kann man das erreichen?
Irgendwann am
1
Pass auf, v ist nur eine Kopie. Wenn du also so etwas wie newMap [k] = & v machst, funktioniert es nicht (es gibt immer den letzten Wert in der Schleife zurück). Kinderleichter Weg wäre newMap [k] = & originalMap [k]
Alekc
20

Ich würde Rekursion nur für den Fall verwenden, damit Sie das tief kopieren mapund böse Überraschungen vermeiden können, falls Sie ein mapElement ändern sollten , das mapselbst ist.

Hier ist ein Beispiel in einer utils.go :

package utils

func CopyMap(m map[string]interface{}) map[string]interface{} {
    cp := make(map[string]interface{})
    for k, v := range m {
        vm, ok := v.(map[string]interface{})
        if ok {
            cp[k] = CopyMap(vm)
        } else {
            cp[k] = v
        }
    }

    return cp
}

Und seine Testdatei (dh utils_test.go ):

package utils

import (
    "testing"

    "github.com/stretchr/testify/require"
)

func TestCopyMap(t *testing.T) {
    m1 := map[string]interface{}{
        "a": "bbb",
        "b": map[string]interface{}{
            "c": 123,
        },
    }

    m2 := CopyMap(m1)

    m1["a"] = "zzz"
    delete(m1, "b")

    require.Equal(t, map[string]interface{}{"a": "zzz"}, m1)
    require.Equal(t, map[string]interface{}{
        "a": "bbb",
        "b": map[string]interface{}{
            "c": 123,
        },
    }, m2)
}

Die Anpassung sollte einfach genug sein, wenn der mapSchlüssel etwas anderes als a sein soll string.

Francesco Casula
quelle
3
Wie oft veröffentlicht jemand eine Lösung mit einer Testdatei! Vielen Dank. Kam hierher für Deep-Copy.
Umang
Sollte dies nicht auch die Slice-Erkennung einschließen, da Slices den gleichen Effekt haben können?
Nathan F.
2

Sie müssen jedes Schlüssel / Wert-Paar manuell in ein neues kopieren map. Dies ist eine Schleife, die Benutzer jedes Mal neu programmieren müssen, wenn sie eine tiefe Kopie von a wünschen map.

Sie können die Funktion hierfür automatisch generieren, indem Sie mit mapperaus dem mapsPaket installieren

go get -u github.com/drgrib/maps/cmd/mapper

und läuft

mapper -types string:aStruct

die generiert die Datei map_float_astruct.goenthält nicht nur eine (tief) Copyfür die Karte , sondern auch andere „fehlenden“ mapFunktionen ContainsKey, ContainsValue, GetKeysund GetValues:

func ContainsKeyStringAStruct(m map[string]aStruct, k string) bool {
    _, ok := m[k]
    return ok
}

func ContainsValueStringAStruct(m map[string]aStruct, v aStruct) bool {
    for _, mValue := range m {
        if mValue == v {
            return true
        }
    }

    return false
}

func GetKeysStringAStruct(m map[string]aStruct) []string {
    keys := []string{}

    for k, _ := range m {
        keys = append(keys, k)
    }

    return keys
}

func GetValuesStringAStruct(m map[string]aStruct) []aStruct {
    values := []aStruct{}

    for _, v := range m {
        values = append(values, v)
    }

    return values
}

func CopyStringAStruct(m map[string]aStruct) map[string]aStruct {
    copyMap := map[string]aStruct{}

    for k, v := range m {
        copyMap[k] = v
    }

    return copyMap
}

Vollständige Offenlegung: Ich bin der Schöpfer dieses Tools. Ich habe es und das darin enthaltene Paket erstellt, weil ich diese Algorithmen für Go mapfür verschiedene Typkombinationen ständig neu geschrieben habe .

Chris Redford
quelle
1

Einzelelementkopie, es scheint für mich nur mit einem einfachen Beispiel zu funktionieren.

maps := map[string]int {
    "alice":12,
    "jimmy":15,
}

maps2 := make(map[string]int)
for k2,v2 := range maps {
    maps2[k2] = v2
}

maps2["miki"]=rand.Intn(100)

fmt.Println("maps: ",maps," vs. ","maps2: ",maps2)
Los Los
quelle
1

Wie in seongs Kommentar angegeben :

Siehe auch http://golang.org/doc/effective_go.html#maps . Der wichtige Teil ist wirklich der "Verweis auf die zugrunde liegende Datenstruktur". Dies gilt auch für Scheiben.

Keine der hier aufgeführten Lösungen scheint jedoch eine Lösung für eine ordnungsgemäße Tiefenkopie zu bieten, die auch Slices abdeckt.

Ich habe die Antwort von Francesco Casula leicht geändert , um sowohl Karten als auch Slices zu berücksichtigen.


Dies sollte sowohl das Kopieren Ihrer Karte selbst als auch das Kopieren von untergeordneten Karten oder Slices umfassen. Beide sind von demselben Problem der "zugrunde liegenden Datenstruktur" betroffen. Es enthält auch eine Dienstprogrammfunktion zum direkten Ausführen derselben Art von Deep Copy für ein Slice.

Beachten Sie, dass die Scheiben in der resultierenden Karte vom Typ sein []interface{}, so dass , wenn sie verwenden, müssen Sie verwenden Typ Behauptung den Wert in der erwarteten Art abzurufen.

Beispiel Verwendung

copy := CopyableMap(originalMap).DeepCopy()

Quelldatei ( util.go)

package utils

type CopyableMap   map[string]interface{}
type CopyableSlice []interface{}

// DeepCopy will create a deep copy of this map. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (m CopyableMap) DeepCopy() map[string]interface{} {
    result := map[string]interface{}{}

    for k,v := range m {
        // Handle maps
        mapvalue,isMap := v.(map[string]interface{})
        if isMap {
            result[k] = CopyableMap(mapvalue).DeepCopy()
            continue
        }

        // Handle slices
        slicevalue,isSlice := v.([]interface{})
        if isSlice {
            result[k] = CopyableSlice(slicevalue).DeepCopy()
            continue
        }

        result[k] = v
    }

    return result
}

// DeepCopy will create a deep copy of this slice. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (s CopyableSlice) DeepCopy() []interface{} {
    result := []interface{}{}

    for _,v := range s {
        // Handle maps
        mapvalue,isMap := v.(map[string]interface{})
        if isMap {
            result = append(result, CopyableMap(mapvalue).DeepCopy())
            continue
        }

        // Handle slices
        slicevalue,isSlice := v.([]interface{})
        if isSlice {
            result = append(result, CopyableSlice(slicevalue).DeepCopy())
            continue
        }

        result = append(result, v)
    }

    return result
}

Testdatei ( util_tests.go)

package utils

import (
    "testing"

    "github.com/stretchr/testify/require"
)

func TestCopyMap(t *testing.T) {
    m1 := map[string]interface{}{
        "a": "bbb",
        "b": map[string]interface{}{
            "c": 123,
        },
        "c": []interface{} {
            "d", "e", map[string]interface{} {
                "f": "g",
            },
        },
    }

    m2 := CopyableMap(m1).DeepCopy()

    m1["a"] = "zzz"
    delete(m1, "b")
    m1["c"].([]interface{})[1] = "x"
    m1["c"].([]interface{})[2].(map[string]interface{})["f"] = "h"

    require.Equal(t, map[string]interface{}{
        "a": "zzz", 
        "c": []interface{} {
            "d", "x", map[string]interface{} {
                "f": "h",
            },
        },
    }, m1)
    require.Equal(t, map[string]interface{}{
        "a": "bbb",
        "b": map[string]interface{}{
            "c": 123,
        },
        "c": []interface{} {
            "d", "e", map[string]interface{} {
                "f": "g",
            },
        },
    }, m2)
}
Nathan F.
quelle