{"id":13413344,"url":"https://github.com/sgrodriguez/ddt","last_synced_at":"2025-03-14T19:31:59.771Z","repository":{"id":57584281,"uuid":"265580853","full_name":"sgrodriguez/ddt","owner":"sgrodriguez","description":"Golang Dynamic Decision Tree","archived":false,"fork":false,"pushed_at":"2021-02-22T12:47:34.000Z","size":411,"stargazers_count":35,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-07-31T20:52:16.021Z","etag":null,"topics":["decision","decision-tree","decison-trees","go","golang","machine-learning","tree","user-tree"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sgrodriguez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-20T13:51:42.000Z","updated_at":"2024-05-31T06:50:40.000Z","dependencies_parsed_at":"2022-09-10T06:51:20.171Z","dependency_job_id":null,"html_url":"https://github.com/sgrodriguez/ddt","commit_stats":null,"previous_names":["sgrodriguez/go-ddt"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrodriguez%2Fddt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrodriguez%2Fddt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrodriguez%2Fddt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sgrodriguez%2Fddt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sgrodriguez","download_url":"https://codeload.github.com/sgrodriguez/ddt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243635371,"owners_count":20322927,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decision","decision-tree","decison-trees","go","golang","machine-learning","tree","user-tree"],"created_at":"2024-07-30T20:01:38.248Z","updated_at":"2025-03-14T19:31:59.446Z","avatar_url":"https://github.com/sgrodriguez.png","language":"Go","readme":"[![Go Report Card](https://goreportcard.com/badge/github.com/sgrodriguez/ddt)](https://goreportcard.com/report/github.com/sgrodriguez/ddt)\n[![codecov](https://codecov.io/gh/sgrodriguez/ddt/branch/master/graph/badge.svg?token=8JU0YG71WZ)](https://codecov.io/gh/sgrodriguez/ddt)\n[![Build Status](https://travis-ci.com/sgrodriguez/ddt.svg?branch=master)](https://travis-ci.com/sgrodriguez/ddt)\n[![Go Reference](https://pkg.go.dev/badge/github.com/sgrodriguez/ddt.svg)](https://pkg.go.dev/github.com/sgrodriguez/ddt)\n# DDT\n## Dynamic decision tree\nDDT allows building custom decision trees based in a set of defined rules, programmatically or from json.\n\nWhen making a decision, it allows adding a pre-processing stage to the input before comparing it with the following possible branches of the tree.\n \nOne of the default preprocessing functions in ddt is calling a method of a struct (CallStructMethod) and getting the attribute of a struct (GetStructAttribute) using reflection.\n\n## Examples\n### Create user tree\nUse a struct as input of the tree and pre-process data before comparing with the next level of nodes.\nIn this example we use some methods and attributes of the user struct.  \n![alt text](docs/user_tree.png?raw=true)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/sgrodriguez/ddt\"\n\t\"github.com/sgrodriguez/ddt/compare\"\n\t\"github.com/sgrodriguez/ddt/function\"\n\t\"github.com/sgrodriguez/ddt/value\"\n)\n\ntype user struct {\n\tAge       int\n\tFirstName string\n\tLastName  string\n}\n\nfunc (u *user) UnderAge() bool {\n\treturn u.Age \u003c 18\n}\n\nfunc (u *user) FullName() string {\n\treturn u.FirstName + \" \" + u.LastName\n}\n\nfunc main() {\n\tnode6 := \u0026ddt.Node{\n\t\tID:             6,\n\t\tParentID:       2,\n\t\tValueToCompare: \u0026value.Value{Type: value.Int, Value: 30},\n\t\tResult:         \u0026value.Value{Type: value.String, Value: \"node6\"},\n\t\tComparer:       \u0026compare.Greater{},\n\t}\n\tnode5 := \u0026ddt.Node{\n\t\tID:             5,\n\t\tParentID:       2,\n\t\tValueToCompare: \u0026value.Value{Type: value.Int, Value: 30},\n\t\tResult:         \u0026value.Value{Type: value.String, Value: \"node5\"},\n\t\tComparer:       \u0026compare.Lesser{Equal: true},\n\t}\n\tnode3 := \u0026ddt.Node{\n\t\tID:             3,\n\t\tParentID:       1,\n\t\tValueToCompare: \u0026value.Value{Type: value.String, Value: \"SANTIAGO LUCIA\"},\n\t\tResult:         \u0026value.Value{Type: value.String, Value: \"node3\"},\n\t\tComparer:       \u0026compare.Equal{},\n\t}\n\tnode4 := \u0026ddt.Node{\n\t\tID:             4,\n\t\tParentID:       1,\n\t\tValueToCompare: \u0026value.Value{Type: value.String, Value: \"LUCIA SANTIAGO\"},\n\t\tResult:         \u0026value.Value{Type: value.String, Value: \"node4\"},\n\t\tComparer:       \u0026compare.Equal{},\n\t}\n\tnode1 := \u0026ddt.Node{\n\t\tID:             1,\n\t\tParentID:       0,\n\t\tChildren:       []*ddt.Node{node3, node4},\n\t\tValueToCompare: \u0026value.Value{Type: value.Bool, Value: true},\n\t\tPreProcessArgs: []*value.Value{{Type: value.String, Value: \"FullName\"}},\n\t\tPreProcessFn:   function.PreProcessFn{Function: function.CallStructMethod, Name: \"CallStructMethod\"},\n\t\tComparer:       \u0026compare.Equal{},\n\t}\n\tnode2 := \u0026ddt.Node{\n\t\tID:             2,\n\t\tParentID:       0,\n\t\tChildren:       []*ddt.Node{node5, node6},\n\t\tValueToCompare: \u0026value.Value{Type: value.Bool, Value: false},\n\t\tComparer:       \u0026compare.Equal{},\n\t\tPreProcessArgs: []*value.Value{{Type: value.String, Value: \"Age\"}},\n\t\tPreProcessFn:   function.PreProcessFn{Function: function.GetStructAttribute, Name: \"GetStructAttribute\"},\n\t}\n\troot := \u0026ddt.Node{\n\t\tChildren:       []*ddt.Node{node1, node2},\n\t\tPreProcessArgs: []*value.Value{{Type: value.String, Value: \"UnderAge\"}},\n\t\tPreProcessFn:   function.PreProcessFn{Function: function.CallStructMethod, Name: \"CallStructMethod\"},\n\t\tID:             0,\n\t\tParentID:       -1,\n\t}\n\tuserTree, err := ddt.NewTree(\"userTree\", root)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tresult, err := ddt.ResolveTree(userTree, \u0026user{Age: 12, FirstName: \"SANTIAGO\", LastName: \"LUCIA\"})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// result node3\n\tfmt.Println(result.(string))\n}\n```\n### Create the user tree from json\n```go\npackage main\n\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/sgrodriguez/ddt\"\n)\n\ntype user struct {\n\tAge       int\n\tFirstName string\n\tLastName  string\n}\n\nfunc (u *user) UnderAge() bool {\n\treturn u.Age \u003c 18\n}\n\nfunc (u *user) FullName() string {\n\treturn u.FirstName + \" \" + u.LastName\n}\n\nfunc main() {\n\t// define empty tree\n\ttree, err := ddt.NewTree(\"newTree\", \u0026ddt.Node{ID: 0, ParentID: -1})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttreeFromJson := []byte(`\n{\n   \"nodes\":[\n      {\n         \"preProcessFnName\":\"CallStructMethod\",\n         \"id\":0,\n         \"parentId\":-1,\n         \"preProcessFnArgs\":[\n            {\n               \"Value\":\"UnderAge\",\n               \"Type\":\"string\"\n            }\n         ]\n      },\n      {\n         \"preProcessFnName\":\"CallStructMethod\",\n         \"id\":1,\n         \"parentId\":0,\n         \"preProcessFnArgs\":[\n            {\n               \"Value\":\"FullName\",\n               \"Type\":\"string\"\n            }\n         ],\n         \"comparer\":{\n            \"type\":\"eq\"\n         },\n         \"valueToCompare\":{\n            \"Value\":true,\n            \"Type\":\"bool\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"GetStructAttribute\",\n         \"id\":2,\n         \"parentId\":0,\n         \"preProcessFnArgs\":[\n            {\n               \"Value\":\"Age\",\n               \"Type\":\"string\"\n            }\n         ],\n         \"comparer\":{\n            \"type\":\"eq\"\n         },\n         \"valueToCompare\":{\n            \"Value\":false,\n            \"Type\":\"bool\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":3,\n         \"parentId\":1,\n         \"comparer\":{\n            \"type\":\"eq\"\n         },\n         \"valueToCompare\":{\n            \"Value\":\"SANTIAGO LUCIA\",\n            \"Type\":\"string\"\n         },\n         \"result\":{\n            \"Value\":\"node3\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":4,\n         \"parentId\":1,\n         \"comparer\":{\n            \"type\":\"eq\"\n         },\n         \"valueToCompare\":{\n            \"Value\":\"LUCIA SANTIAGO\",\n            \"Type\":\"string\"\n         },\n         \"result\":{\n            \"Value\":\"node4\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":5,\n         \"parentId\":2,\n         \"comparer\":{\n            \"type\":\"lt\",\n            \"equal\":true\n         },\n         \"valueToCompare\":{\n            \"Value\":30,\n            \"Type\":\"int\"\n         },\n         \"result\":{\n            \"Value\":\"node5\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":6,\n         \"parentId\":2,\n         \"comparer\":{\n            \"type\":\"gt\",\n            \"equal\":false\n         },\n         \"valueToCompare\":{\n            \"Value\":30,\n            \"Type\":\"int\"\n         },\n         \"result\":{\n            \"Value\":\"node6\",\n            \"Type\":\"string\"\n         }\n      }\n   ],\n   \"name\":\"userTree\"\n}`)\n\terr = json.Unmarshal(treeFromJson, tree)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tresult, err := ddt.ResolveTree(tree, \u0026user{Age: 12, FirstName: \"SANTIAGO\", LastName: \"LUCIA\"})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// result node3\n\tfmt.Println(result.(string))\n\ttreeByte, err := json.Marshal(tree)\n\tfmt.Println(string(treeByte))\n}\n```\n### Create simple tree\nCreate a simple tree using only basic types.\n![alt text](docs/simple_tree.png?raw=true)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/sgrodriguez/ddt\"\n\t\"github.com/sgrodriguez/ddt/compare\"\n\t\"github.com/sgrodriguez/ddt/value\"\n)\n\nfunc main() {\n\tleaf1 := ddt.Node{\n\t\tID:             1,\n\t\tParentID:       0,\n\t\tValueToCompare: \u0026value.Value{Value: int64(60), Type: value.Int64},\n\t\tComparer:       \u0026compare.Greater{},\n\t\tResult:         \u0026value.Value{Value: \"prize1\", Type: value.String},\n\t}\n\tleaf11 := ddt.Node{\n\t\tID:             3,\n\t\tParentID:       2,\n\t\tValueToCompare: \u0026value.Value{Value: int64(30), Type: value.Int64},\n\t\tComparer:       \u0026compare.Equal{},\n\t\tResult:         \u0026value.Value{Value: \"prize2\", Type: value.String},\n\t}\n\tleaf12 := ddt.Node{\n\t\tID:             4,\n\t\tParentID:       2,\n\t\tValueToCompare: \u0026value.Value{Value: int64(30), Type: value.Int64},\n\t\tComparer:       \u0026compare.Greater{},\n\t\tResult:         \u0026value.Value{Value: \"prize3\", Type: value.String},\n\t}\n\tleaf13 := ddt.Node{\n\t\tID:             5,\n\t\tParentID:       2,\n\t\tValueToCompare: \u0026value.Value{Value: int64(30), Type: value.Int64},\n\t\tComparer:       \u0026compare.Lesser{},\n\t\tResult:         \u0026value.Value{Value: \"prize4\", Type: value.String},\n\t}\n\tnode1 := ddt.Node{\n\t\tChildren:       []*ddt.Node{\u0026leaf11, \u0026leaf12, \u0026leaf13},\n\t\tID:             2,\n\t\tParentID:       0,\n\t\tValueToCompare: \u0026value.Value{Value: int64(60), Type: value.Int64},\n\t\tComparer:       \u0026compare.Lesser{Equal: true},\n\t}\n\troot := ddt.Node{\n\t\tID:       0,\n\t\tParentID: -1,\n\t\tChildren: []*ddt.Node{\u0026node1, \u0026leaf1},\n\t}\n\tsimpleTree, err := ddt.NewTree(\"simpleTree\", \u0026root)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tresult, err := ddt.ResolveTree(simpleTree,int64(15))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(result.(string))\n}\n```\n### Create simple tree from json\nCreate or modify the simple tree from json\n```go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/sgrodriguez/ddt\"\n)\n\nfunc main() {\n\t// define empty tree\n\ttree, err := ddt.NewTree(\"newTree\", \u0026ddt.Node{ID: 0, ParentID: -1})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttreeFromJson := []byte(`\n{\n   \"nodes\":[\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":0,\n         \"parentId\":-1\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":2,\n         \"parentId\":0,\n         \"comparer\":{\n            \"type\":\"lt\",\n            \"equal\":true\n         },\n         \"valueToCompare\":{\n            \"Value\":60,\n            \"Type\":\"int64\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":1,\n         \"parentId\":0,\n         \"comparer\":{\n            \"type\":\"gt\",\n            \"equal\":false\n         },\n         \"valueToCompare\":{\n            \"Value\":60,\n            \"Type\":\"int64\"\n         },\n         \"result\":{\n            \"Value\":\"prize1\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":3,\n         \"parentId\":2,\n         \"comparer\":{\n            \"type\":\"eq\"\n         },\n         \"valueToCompare\":{\n            \"Value\":30,\n            \"Type\":\"int64\"\n         },\n         \"result\":{\n            \"Value\":\"prize2\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":4,\n         \"parentId\":2,\n         \"comparer\":{\n            \"type\":\"gt\",\n            \"equal\":false\n         },\n         \"valueToCompare\":{\n            \"Value\":30,\n            \"Type\":\"int64\"\n         },\n         \"result\":{\n            \"Value\":\"prize3\",\n            \"Type\":\"string\"\n         }\n      },\n      {\n         \"preProcessFnName\":\"\",\n         \"id\":5,\n         \"parentId\":2,\n         \"comparer\":{\n            \"type\":\"lt\",\n            \"equal\":false\n         },\n         \"valueToCompare\":{\n            \"Value\":30,\n            \"Type\":\"int64\"\n         },\n         \"result\":{\n            \"Value\":\"prize4\",\n            \"Type\":\"string\"\n         }\n      }\n   ],\n   \"name\":\"simpleTree\"\n}`)\n\terr = json.Unmarshal(treeFromJson, tree)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tresult, err := ddt.ResolveTree(tree, int64(15))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// result prize4\n\tfmt.Println(result.(string))\n\t// change some property of the tree for example the result of prize4\n\tmodifiedTree := []byte(`{\"nodes\":[{\"preProcessFnName\":\"\",\"id\":0,\"parentId\":-1},{\"preProcessFnName\":\"\",\"id\":2,\"parentId\":0,\"comparer\":{\"type\":\"lt\",\"equal\":true},\"valueToCompare\":{\"Value\":60,\"Type\":\"int64\"}},{\"preProcessFnName\":\"\",\"id\":1,\"parentId\":0,\"comparer\":{\"type\":\"gt\",\"equal\":false},\"valueToCompare\":{\"Value\":60,\"Type\":\"int64\"},\"result\":{\"Value\":\"prize1\",\"Type\":\"string\"}},{\"preProcessFnName\":\"\",\"id\":3,\"parentId\":2,\"comparer\":{\"type\":\"eq\"},\"valueToCompare\":{\"Value\":30,\"Type\":\"int64\"},\"result\":{\"Value\":\"prize2\",\"Type\":\"string\"}},{\"preProcessFnName\":\"\",\"id\":4,\"parentId\":2,\"comparer\":{\"type\":\"gt\",\"equal\":false},\"valueToCompare\":{\"Value\":30,\"Type\":\"int64\"},\"result\":{\"Value\":\"prize3\",\"Type\":\"string\"}},{\"preProcessFnName\":\"\",\"id\":5,\"parentId\":2,\"comparer\":{\"type\":\"lt\",\"equal\":false},\"valueToCompare\":{\"Value\":30,\"Type\":\"int64\"},\"result\":{\"Value\": 420,\"Type\":\"int64\"}}],\"name\":\"simpleTree\"}`)\n\terr = json.Unmarshal(modifiedTree, tree)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tresult, err = ddt.ResolveTree(tree, int64(15))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// result 420\n\tfmt.Println(result.(int64))\n}\n```\n\n## Overview\n#### Node\n* ID: id of the node, root node must have 0.\n* ParentID: parent id, root node must have -1.\n* Result: if the node is leaf and is the next node of the tree, this is the result.\n* Comparer.\n* ValueToCompare: value \n* PreProcessFn: function to pre-process the input before comparing.\n* PreProcessArgs.\n    \n#### Value\nBasic types available for comparing, result and as PreProcessArgs.\n   * Int\n   * Int64\n   * Bool\n   * String\n   * Float64\n#### Comparators\n   * Greater (or Equal)\n   * Lesser  (or Equal)\n   * Equal\n#### Pre-Process Functions\nFunctions to pre-process the input before comparing with the next level of the tree.\n   * CallStructMethod \n   * GetStructAttribute \n\n\n","funding_links":[],"categories":["Machine Learning","机器学习","Relational Databases"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","Advanced Console UIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgrodriguez%2Fddt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsgrodriguez%2Fddt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsgrodriguez%2Fddt/lists"}