Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement arrayok types #24

Merged
merged 18 commits into from
Aug 21, 2024
Merged

Implement arrayok types #24

merged 18 commits into from
Aug 21, 2024

Conversation

MetalBlueberry
Copy link
Owner

@MetalBlueberry MetalBlueberry commented May 19, 2024

this is an attempt to implement arrayok types. This example just covers float64, but it should be easy to extrapolate to any other type.

The idea is that if the Array is defined, it will use that to create the json file and otherwise it will use the Value.

When using this to write code, it just introduces a small indirection with the advantage of keeping the type safety.

@PatrickVienne
Copy link
Contributor

@MetalBlueberry

Think it goes in a good direction, there are still missing use cases.

  • colors as array
  • text as array
  • nil values in the middle of scatter/line plot y value arrays which make it possible to add plots with gaps.

exposing all features of JS which are built on flexibility of types, and maintaining type safety are very hard to reconcile.

We could introduce structs, with additional arrays with pointers to enable nil, and then while marshalling we could retrieve the value of elements which are set.

That said, this all seems to me like trying to fit a square peg in a round hole.

@MetalBlueberry
Copy link
Owner Author

What do you thing about the generics approach? You can define the type for anything you want and use it the same way it uses strings and float64.

@MetalBlueberry
Copy link
Owner Author

I added another case with a custom color struct , again, just as an example of what it can be done

@MetalBlueberry
Copy link
Owner Author

And last but not least, a case to reflect null values

@PatrickVienne
Copy link
Contributor

@MetalBlueberry
Thanks a lot, you put lots of work into this to make it user friendly.
As an example, I'm trying to setup a chart in goplotly with the following outcome:

HTML:

<head>
  <!-- Plotly.js -->
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>

<body>
  
  <div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
  <script>
    <!-- JAVASCRIPT CODE GOES HERE -->
  </script>
</body>

JS:

var trace1 = {
  x: [1, 2, 3, 4, 5, 6, 7, 8],
  y: [10, 15, null, 17, 14, 12, 10, null, 15],
  mode: 'lines+markers',
  connectgaps: true
};

var trace2 = {
  x: [1, 2, 3, 4, 5, 6, 7, 8],
  y: [16, null, 13, 10, 8, null, 11, 12],
  mode: 'lines',
  connectgaps: true
};

var trace3 = {
  x: [1, 2, 3, null, null, 6, 7, 8],
  y: [2, 2, 2, 1, 1, 3, 3, 4],
  mode: 'lines',
};

var data = [trace1, trace2, trace3];

var layout = {
  title: 'Connect the Gaps Between Data',
  showlegend: false
};

Plotly.newPlot('myDiv', data, layout, {showSendToCloud: true});

newplot

I'm still unsure how to achieve this.
Could you make an example?

@MetalBlueberry
Copy link
Owner Author

Damm,
I'm just reading from my phone and now I understand the problem.

I will think about this

@MetalBlueberry
Copy link
Owner Author

MetalBlueberry commented May 25, 2024

One idea is

package main

import (
    "github.com/MetalBlueberry/go-plotly/graph_objects"
    "github.com/MetalBlueberry/go-plotly/offline"
    "github.com/MetalBlueberry/go-plotly/types"
)

func main() {
    trace1 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: []interface{}{1, 2, 3, 4, 5, 6, 7, 8}},
        Y: types.ArrayOK{Array: []interface{}{10, 15, nil, 17, 14, 12, 10, nil, 15}},
        Mode: graph_objects.ScatterModeEnum.LinesMarkers,
        Connectgaps: true,
    }

    trace2 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: []interface{}{1, 2, 3, 4, 5, 6, 7, 8}},
        Y: types.ArrayOK{Array: []interface{}{16, nil, 13, 10, 8, nil, 11, 12}},
        Mode: graph_objects.ScatterModeEnum.Lines,
        Connectgaps: true,
    }

    trace3 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: []interface{}{1, 2, 3, nil, nil, 6, 7, 8}},
        Y: types.ArrayOK{Array: []interface{}{2, 2, 2, 1, 1, 3, 3, 4}},
        Mode: graph_objects.ScatterModeEnum.Lines,
    }

    data := []graph_objects.Trace{trace1, trace2, trace3}

    layout := &graph_objects.Layout{
        Title: graph_objects.LayoutTitle.Text("Connect the Gaps Between Data"),
        Showlegend: false,
    }

    plot := offline.NewPlot(data, layout, offline.ShowLink(false), offline.LinkText(""), offline.ScrollZoom(true))
    plot.SaveAsHTML("myDiv")
}

But it requires interface{}. which I dont' really like

So another idea is

package main

import (
    "github.com/MetalBlueberry/go-plotly/graph_objects"
    "github.com/MetalBlueberry/go-plotly/offline"
    "github.com/MetalBlueberry/go-plotly/types"
)

func main() {
    x1 := []int{1, 2, 3, 4, 5, 6, 7, 8}
    y1 := []*int{intPtr(10), intPtr(15), nil, intPtr(17), intPtr(14), intPtr(12), intPtr(10), nil, intPtr(15)}
    trace1 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: x1},
        Y: types.ArrayOK{Array: y1},
        Mode: graph_objects.ScatterModeEnum.LinesMarkers,
        Connectgaps: true,
    }

    x2 := []int{1, 2, 3, 4, 5, 6, 7, 8}
    y2 := []*int{intPtr(16), nil, intPtr(13), intPtr(10), intPtr(8), nil, intPtr(11), intPtr(12)}
    trace2 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: x2},
        Y: types.ArrayOK{Array: y2},
        Mode: graph_objects.ScatterModeEnum.Lines,
        Connectgaps: true,
    }

    x3 := []*int{intPtr(1), intPtr(2), intPtr(3), nil, nil, intPtr(6), intPtr(7), intPtr(8)}
    y3 := []int{2, 2, 2, 1, 1, 3, 3, 4}
    trace3 := &graph_objects.Scatter{
        X: types.ArrayOK{Array: x3},
        Y: types.ArrayOK{Array: y3},
        Mode: graph_objects.ScatterModeEnum.Lines,
    }

    data := []graph_objects.Trace{trace1, trace2, trace3}

    layout := &graph_objects.Layout{
        Title: graph_objects.LayoutTitle.Text("Connect the Gaps Between Data"),
        Showlegend: false,
    }

    plot := offline.NewPlot(data, layout, offline.ShowLink(false), offline.LinkText(""), offline.ScrollZoom(true))
    plot.SaveAsHTML("myDiv")
}

func intPtr(i int) *int {
    return &i
}

But it is horrible to read.
It is going to be hard to find a middle ground.... but I'll think a bit more about this

Check the test cases for other examples

@PatrickVienne
Copy link
Contributor

maybe something along these lines:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

type Nullable interface {
	isNullable()
}

type NullableFloat float64

func (g *NullableFloat) isNullable() {}

type NullableString string

func (g *NullableString) isNullable() {}

var nullables []Nullable

func main() {
	num1 := NullableFloat(1)
	text1 := NullableString("1")
	nullables = append(nullables, nil, &num1, &text1)

	buf := bytes.NewBuffer(make([]byte, 0))
	enc := json.NewEncoder(buf)
	err := enc.Encode(nullables)
	if err != nil {
		panic(err)
	}
	fmt.Println("nullables:", buf)
}

Output:

nullables: [null,1,"1"]

I put it in a golang playground:
https://play.golang.com/p/G3_EA8g6kgc

@MetalBlueberry
Copy link
Owner Author

Well, it has been a while. But i finally found time and inspiration to finish this PR.

I think the arrayOK implementation meets the requirements you had when you first open the PR without loosing too much readibility and comfort. I recommend you to take a look to the examples directory to see how the arrayok type affects the schema.

Thank you for opening the PR ❤️

@MetalBlueberry
Copy link
Owner Author

Also I realised the ArrayOK is not used for X and Y values. those are data_arrays that will require a difirent implementation

@MetalBlueberry MetalBlueberry merged commit b2d107e into master Aug 21, 2024
3 checks passed
@MetalBlueberry MetalBlueberry changed the title Try to implement arrayok types Implement arrayok types Aug 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants