dominikbraun / graph

A library for creating generic graph data structures and modifying, analyzing, and visualizing them.
https://graph.dominikbraun.io
Apache License 2.0
1.81k stars 94 forks source link

VertexAttributes func caching last values #177

Open davemcphee opened 4 months ago

davemcphee commented 4 months ago

Calling AddVertex() with VertexAttribute() functions will sometimes cache the last value on all vertices. I've been unable to reproduce this via your unit tests, but this shows the issue:

package main

import (
    "bytes"
    "fmt"
    "github.com/dominikbraun/graph"
    "github.com/dominikbraun/graph/draw"
)

var (
    hashingFunction = func(d someVertex) string { return d.Name }
    redVertexAttr   = graph.VertexAttributes(map[string]string{"color": "red"})
    dotBuffer       bytes.Buffer
)

type someVertex struct {
    Name string
}

func main() {

    g := graph.New(hashingFunction, graph.Directed())

    for i, name := range []string{"foo", "bar", "baz"} {
        vertex := someVertex{Name: name}

        label := fmt.Sprintf("%s [%d]", name, i)
        fmt.Printf("adding someVertex{Name: %s} with label %s\n", name, label)

        _ = g.AddVertex(
            vertex,
            redVertexAttr,
            graph.VertexAttribute("label", label),
        )

        // read the new vertex back
        newVertex, newVertexProps, _ := g.VertexWithProperties(vertex.Name)
        fmt.Printf("new Vertex %s: %#v\nProps: %#v\n\n", vertex.Name, newVertex, newVertexProps)
    }

        fmt.Println("Here be issues:")

    fooVertex, fooProps, _ := g.VertexWithProperties("bar")
    fmt.Printf("Vertex bar: %#v\n", fooVertex)
    fmt.Printf("Vertex bar properties: %#v\n", fooProps)

    if dotErr := draw.DOT(g, &dotBuffer); dotErr != nil {
        panic(dotErr)
    }

    fmt.Printf("\n%s", dotBuffer.String())
}

---

adding someVertex{Name: foo} with label foo [0]
new Vertex foo: main.someVertex{Name:"foo"}
Props: graph.VertexProperties{Attributes:map[string]string{"color":"red", "label":"foo [0]"}, Weight:0}

adding someVertex{Name: bar} with label bar [1]
new Vertex bar: main.someVertex{Name:"bar"}
Props: graph.VertexProperties{Attributes:map[string]string{"color":"red", "label":"bar [1]"}, Weight:0}

adding someVertex{Name: baz} with label baz [2]
new Vertex baz: main.someVertex{Name:"baz"}
Props: graph.VertexProperties{Attributes:map[string]string{"color":"red", "label":"baz [2]"}, Weight:0}

Here be issues:
Vertex bar: main.someVertex{Name:"bar"}
Vertex bar properties: graph.VertexProperties{Attributes:map[string]string{"color":"red", "label":"baz [2]"}, Weight:0}

strict digraph {

        "baz" [ color="red", label="baz [2]",  weight=0 ];

        "foo" [ color="red", label="baz [2]",  weight=0 ];

        "bar" [ color="red", label="baz [2]",  weight=0 ];

}

Each vertices' Attributes map contains the Attributes of the last created vertex. Am I creating these VertexProperties functions incorrectly, or mis-using them?

tzq0301 commented 3 months ago

Attributes map of all VertexProperties share the same map declared in the line of:

    redVertexAttr   = graph.VertexAttributes(map[string]string{"color": "red"})

This was a bug of the library, and I fixed it in PR https://github.com/dominikbraun/graph/pull/179, cc. @dominikbraun