In my use case, I would like to know how the following Java code would be implemented in Go
class TreeNode {
public int data;
public TreeNode left;
public TreeNode right;
public TreeNode(){}
}
LinkedList<TreeNode> treeList = new LinkedList<TreeNode>();
I am able to import the container/list package and add an interface. But it is not allowing any generic object. Do I have to implement my own version of list with TreeNode struct?
I just need to know how LinkedList<T> would work in Go.
EDIT 1: To make it clear, I am adding the complete code here. I am trying to find the linked list of all nodes at each depth in a binary tree. I used two packages list and binary tree. You can find the source code for binarytree here and list here. list is same as container/list but I added few extra functions
package main
import (
"fmt"
"go/chapter02-linkedlists/list"
"go/chapter04-treesandgraphs/binarytree"
)
func main() {
inArr := []int{4, 5, 7, 8, 9}
t1 := binarytree.NewMinimalHeightBST(inArr, 0, len(inArr)-1)
binarytree.InOrderTraverse(t1)
var nodeList []*list.List
nodeList = getLevelbasedList(t1, 0)
fmt.Println()
for _, value := range nodeList {
fmt.Print("[ ")
for x := value.Front(); x != nil; x = x.Next() {
fmt.Print(x.Value.(int), " ")
}
fmt.Println("]")
}
}
func getLevelbasedList(root *binarytree.Tree, level int) []*list.List {
if root == nil {
return nil
}
var nodeList []*list.List
parents := list.New()
current := list.New()
current.PushFront(root)
for current.Len() > 0 {
nodeList = append(nodeList, current)
parents = current
current = list.New()
for x := current.Front(); x != nil; x = x.Next() {
node := x.Value.(*binarytree.Tree)
if node.Left != nil {
current = current.PushFront(node.Left)
}
if node.Right != nil {
current = current.PushFront(node.Right)
}
}
return nodeList
}
}
And the error is,
./question4_4b.go:56: cannot use current.PushFront((interface {})(node.Left)) (type *list.Element) as type *list.List in assignment
./question4_4b.go:59: cannot use current.PushFront((interface {})(node.Right)) (type *list.Element) as type *list.List in assignment
EDIT 2: Based on JamesHenstridge's comment I edited from
current = current.PushFront(node.Left)
to
current.PushFront(node.Left)
And the issue resolved. But now I am getting interface conversion error,
[ panic: interface conversion: interface is *binarytree.Tree, not int
goroutine 1 [running]:
Go doesn't support generic types (see FAQ question Why does Go not have generic types?).
You have to use Type assertions to obtain the typed value you want.
E.g. create your TreeNode type:
type TreeNode struct {
Data int
Left *TreeNode
Right *TreeNode
}
And to iterate over a list containing TreeNode values:
l := list.New()
// Populate list
for e := l.Front(); e != nil; e = e.Next() {
if tn, ok := e.Value.(TreeNode); ok {
// do something with tn which is of type TreeNode
fmt.Println(tn)
} else {
// e.Value is not of type TreeNode
}
}
If you assemble the list and you can be sure it only contains values of type TreeNode, you can omit the error check in the type assertion and it becomes like this:
for e := l.Front(); e != nil; e = e.Next() {
// if e.Value would not be of type TreeNode, run-time panic would occur
tn := e.Value.(TreeNode) // tn is of type TreeNode
fmt.Println(tn)
}
Edit:
The error you're getting:
cannot use current.PushFront((interface {})(node.Left)) (type *list.Element)
as type *list.List in assignment
At line:
current = current.PushFront(node.Left)
The current variable is of type list.List, and the method current.PushFront() returns a value of type *list.Element. These are 2 different types, you can't assign a *Element to a variable that has a type of List.
Edit 2:
Your 2nd error:
panic: interface conversion: interface is *binarytree.Tree, not int
Is caused by the line:
fmt.Print(x.Value.(int), " ")
You try to assert that the value x.Value is of type int but it isn't! x.Value is of type *binarytree.Tree so the assertion will obviously fail.
Related
New in Go, couldn't find any intuitive way of doing that.
I have such piece of code
tx = getTx()
for _, record := range tx.a {
// do a lot with record.Important
}
for _, record := range tx.b {
// do a lot with record.Important
}
for _, record := range tx.c {
// do a lot with record.Important
}
And the following structs:
type Record1 struct {
// fields of Record1
Important string
}
type Record2 struct {
// fields of Record1
Important string
}
type TX struct {
a []Record1
b []Record1
c []Record2
}
Now the logical is to extract every for logic into the function:
func helper(records) { // Here is the problem
// do a lot with record.Important
}
Problem:
records is a []Record1 or []Record2 type. But it looks like Union types doesn't exists in Golang. So I thought I could pass []string into the helper, but cannot even find an elegant way to get something equivalent to map(lambda r: r.Important, tx.a). There is no high order map function, no list comprehesion. I am not convinced to use raw for loop to solve that.
One approach to do the loop across multiple types is to use interfaces together with generics. Have each Record type implement a getter method for the important field. Then declare an interface that includes that getter method in its method set. Then you can make your helper generic by declaring the interface as its type parameter.
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImporant() string
}
func helper[T ImportantGetter](s []T) {
for _, v := range s {
_ = v.GetImportant()
}
}
Unless I'm misunderstanding your question, it seems like you want to extract all the values in column X from a set of records and then pass those values in as a slice to some function - I'm basing my assumption on your wish that go had something like map().
If what you're after is type-agnosticism, you could certainly use an interface approach like that suggested by mkopriva, but you aren't going to get out of using a for loop - iteration over list types is core to idiomatic go. If you need a mapping function, you're going to have to write one that performs the mapping you want.
I'd note that you do not need generics to do what mkopriva suggests, you can just use an interface without muddying the waters with generics go playground:
package main
import "fmt"
type Record1 struct {
Important string
}
type Record2 struct {
Important string
}
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImportant() string
}
func helper(s []ImportantGetter) {
for _, v := range s {
fmt.Println(v.GetImportant())
}
}
func main() {
records := []ImportantGetter{Record1{Important: "foo"}, Record2{Important: "bar"}}
helper(records)
}
Another approach to the type-agnosticism, and one that's a bit more (IMHO) idiomatic for "I expect all of these types to have a common property," is to use struct embedding and type assertions to build your own Map() function up go playground:
type CommonFields struct {
Important string
}
type Record1 struct {
CommonFields
FieldSpecificToRecord1 string
}
type Record2 struct {
CommonFields
FieldSpecificToRecord2 int
}
func main() {
r1 := Record1{
CommonFields{Important: "I'm r1!"},
"foo",
}
r2 := Record2{
CommonFields{Important: "I'm r2!"},
5,
}
records := []interface{}{r1, r2, "this is not a valid record type"}
fmt.Println(Map(records))
}
func Map(source []interface{}) []string {
destination := make([]string, len(source))
for i, sourceRecord := range source {
if rr, ok := sourceRecord.(Record1); ok {
destination[i] = rr.Important
} else if rr, ok := sourceRecord.(Record2); ok {
destination[i] = rr.Important
} else {
destination[i] = "undefined"
}
}
return destination
}
You'd likely want to make your implementation of Map() accept an argument specifying the field to extract to conform to what you have in other languages, or possibly even just pass in a helper function which does most of the type-specific value extraction.
Given a map that uses a struct as its key, where the values of the struct are pointers to another struct:
type Dog struct {
Name string
}
type Cat struct {
Name string
}
type MapKey struct {
dog *Dog
cat *Cat
}
myMap := make(map[MapKey]int)
How would I use the cmp package to make the below maps equal, where they are considered equal because the MapKey has the same values (reflect.DeepEquals or cmp.Equals)?
keyOne := MapKey{
&Dog{Name: "bob"},
&Cat{Name: "clive"},
}
keyTwo := MapKey{
&Dog{Name: "bob"},
&Cat{Name: "clive"},
}
got := map[MapKey]int{
keyOne: 1,
}
want := map[MapKey]int{
keyTwo: 1,
}
In the cmp documentation, it says I could use cmpopts.SortMaps (https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal), however I don't see how this is relevant to my scenario.
I've tried defining a custom Equals function on the MapKey struct but it never gets called.
Go playground to reproduce this:
https://go.dev/play/p/qMxaya3S26M
The cmp.Equal is called with parameters of type map[MapKey]int, not MapKey
So the custom Equal function has to be defined on the type map[MapKey]int.
But to define this function, we need to define a new type from map[MapKey]int.
Here's a playground to the full working example: https://go.dev/play/p/deteHANWQ_3
type MapKeyInt map[MapKey]int
func (m MapKeyInt) Equal(other MapKeyInt) bool {
if len(m) != len(other) {
return false
}
keys, keysOther := make([]MapKey, 0), make([]MapKey, 0)
values, valuesOther := make([]int, 0), make([]int, 0)
for k, v := range m {
keys = append(keys, k)
values = append(values, v)
}
for k, v := range other {
keysOther = append(keysOther, k)
valuesOther = append(valuesOther, v)
}
for i := 0; i < len(m); i++ {
if (keys[i].dog.Name != keysOther[i].dog.Name) || (keys[i].cat.Name != keysOther[i].cat.Name) {
return false
}
if values[i] != valuesOther[i] {
return false
}
}
return true
}
How to efficiently implement below c++ function in rust? The data structure must be tree based (BTree, RBTree, etc).
Given a sorted map m, a key target, and a value val.
Find the lower_bound entry (the first key >= target). return DEFAULT if no such entry.
If the value of the found entry <= val and it has previous entry, return value of previous entry.
If the value of the found entry > val and it has next entry, return value of the next entry.
Otherwise, return the found value.
template<class K, class V>
V find_neighbor(const std::map<K, V>& m, const K& target, const V& val) {
auto it = m.lower_bound(target);
if( it == m.end() ) return V{}; // DEFAULT value.
if( it->second <= val && it != m.begin() )
return (--it)->value; // return previous value
if( it->second > val && it != (--m.end()) )
return (++it)->value; // return next value
return it->second; // return target value
}
Thats what I've got.
Create trait FindNeighbor that adds the function find_neighbor to all BTreeMaps
I'm quite confused what the algorithm does, though, tbh. But it should (tm) behave identical to the C++ version.
If you use this in an actual project though, for the love of god, please write unit tests for it. 😄
use std::{borrow::Borrow, collections::BTreeMap};
trait FindNeighbor<K, V> {
type Output;
fn find_neighbor(&self, target: K, val: V) -> Self::Output;
}
impl<K, V, KI, VI> FindNeighbor<K, V> for BTreeMap<KI, VI>
where
K: Borrow<KI>,
V: Borrow<VI>,
KI: Ord,
VI: Default + PartialOrd + Clone,
{
type Output = VI;
fn find_neighbor(&self, target: K, val: V) -> VI {
let val: &VI = val.borrow();
let target: &KI = target.borrow();
let mut it = self.range(target..);
match it.next() {
None => VI::default(),
Some((_, it_value)) => {
if it_value <= val {
match self.range(..target).rev().next() {
Some((_, prev_val)) => prev_val.clone(),
None => it_value.clone(),
}
} else {
match it.next() {
Some((_, next_val)) => next_val.clone(),
None => it_value.clone(),
}
}
}
}
}
}
fn main() {
let map = BTreeMap::from([(1, 5), (2, 3), (3, 8)]);
println!("{:?}", map.find_neighbor(3, 10));
}
3
Note a couple of differences between C++ and Rust:
Note that there are trait annotations on the generic parameters. Generic functions work a little different than C++ templates. All the capabilities that get used inside of a generic method have to be annotated as trait capabilities. The advantage is that generics are then guaranteed to work with every type they take, no random compiler errors can occur any more. (C++ templates are more like duck-typing, while Rust generics are strongly typed)
We implement a trait that adds new functionality to an external struct. That is something that also doesn't exist in C++, and tbh I really like this mechanic in Rust.
General problem I'm trying to solve
I'm trying to implement a search tree in Google Apps Script, sorted by pkgName attribute, with the end purpose of comparing imported metadata on a software project against a Sheet containing similar data.
To keep the namespace of the constructor function from being polluted with "private" properties, I used closures.
Implementation
The implementation I have thus far is thus:
SheetDataNode.gs
/**
* Constructor for a SheetDataNode. Takes one, three, or four arguments.
* #param { { package : string, files : { complexity : number, name : string, testingStatus : string }[], rowNumber : number } | string } line data or package name
* #param { string } filename : the files contained in package
* #param { number } complexity : the total number of branches in the file
* #param { number } rowNumber : the row number as this appears in the spreadsheet it is being created from
* #param { string } [ testingStatus ] : the status on the testing of this file. Should be one of the following: NOT_TESTED, FULLY_TESTED, IN_PROGRESS, or PARTIAL
* #returns { SheetDataNode }
* #deprecated This function is not working right now
**/
function SheetDataNode(data, filename, complexity, rowNumber, testingStatus) {
var _pkgName = '';
var _leftChild = null;
var _rightChild = null;
var _beenFound = false;
var _rowNumber = rowNumber;
var _files = [];
// if there's only one argument, it better be an object, having the required fields
if (arguments.length === 1) {
// it should have package field
if ((data.package === undefined) || (data.package !== data.package.toString())) {
throw ReferenceError('only one argument was specified, but it is not an object that contains package');
}
// it should have files field
if ((data.files === undefined) || (!Array.isArray(data.files))) {
throw ReferenceError('Called from the one-arg constructor, so files should be Array');
}
// that files field should itself be an object with the following fields: complexity and name
for (var idx in data.files) {
if (data.files[idx].complexity !== parseInt(data.files[idx].complexity)) {
throw TypeError("complexity should be an integer");
}
if (data.files[idx].name !== data.files[idx].name.toString()) {
throw TypeError("name of file should be a string");
}
}
// sort the array of files
data.files.sort(fileSorter)
// call the initialization function
return SheetDataNode._init(data.package, data.files, parseInt(data.rowNumber));
}
// performing argument checking
if (filename !== filename.toString()) throw TypeError("filename is supposed to be a String")
if ((complexity !== undefined) && (complexity !== parseInt(complexity))) {
throw TypeError("complexity must be a number, or undefined")
}
// call the initialization function, constructing a single file object
return SheetDataNode._init(data.toString(), [{
complexity : complexity,
name: filename,
testingStatus : testingStatus
}])
}
// Helper private function that performs initialization
SheetDataNode._init = function(package, files, rowNumber) {
// bring in the variables
var _pkgName = package;
var _files = files;
var _leftChild = null;
var _rightChild = null;
var _beenFound = false;
var _rowNumber = rowNumber;
// providing a function to add file
_addFile = function(file) {
for (var f in _files) {
if (file.name < _files[f].name) {
_files.splice(f, 0, file)
return
}
}
_files.push(file)
}
return {
getRowNumber : function() { return _rowNumber; },
getPackageName : function () { return _pkgName; },
getFiles: function() { return _files; },
addFile : _addFile,
addFiles : function(files) {
if (!Array.isArray(files)) throw TypeError("files should be an Array")
for (var idx in files) {
_addFile(files[idx])
}
},
getLeftChild : function() { return _leftChild; },
setLeftChild : function(node) {
_leftChild = node;
},
getRightChild : function() { return _rightChild; },
setRightChild : function(node) {
_rightChild = node;
},
insertNode : function(node) {
// set the current node as the head node
var currentNode = this;
// while we are on a non-null node
while (currentNode) {
// if the package of node is the same as that of currentNode
if (currentNode.getPackageName() === node.getPackageName()) {
// simply add the files of node to currentNode._files
currentNode.addFiles(node.getFiles())
return
}
// if the package of node "comes before" that of currentNode, move to the left
if (currentNode.getPackageName() > node.getPackageName()) {
// if the left child of node is defined, that becomes the current node
if (currentNode.getLeftChild()) currentNode = currentNode.getLeftChild()
// else construct it, and we're done
else {
currentNode.setLeftChild(node)
return
}
}
// if the package of node "comes after" that of currentNode, move to the right
if (currentNode.getPackageName() < node.getPackageName()) {
// if the right child of node is defined, that becomes the current node
if (currentNode.getRightChild()) currentNode = currentNode.getRightChild()
// else construct it, and we're done
else {
currentNode.setRightChild(node)
return
}
}
throw Error("Whoa, some infinite looping was about to happen!")
}
}
}
}
UtilityFunctions.gs
/**
* Sorts file objects by their name property, alphabetically
* #param { { name : string } } lvalue
* #param { { name : string } } rvalue
* #returns { boolean } the lexical comparison of lvalue.name,rvalue.name
**/
function fileSorter(lvalue, rvalue) {
if (lvalue.name > rvalue.name) return 1;
return (lvalue.name < rvalue.name) ? -1 : 0;
}
Problem
I'm unit-testing the code, with the failing test case consisting of the following steps :
construct a SheetDataNode node
construct another SheetDataNode otherNode with the same package name as the first, but different filename
insert otherNode into node
expectation: it now has two files
actual: it only has one: the original.
expectation: neither left nor right child nodes were set by this operation
actual : neither left nor right child nodes were set by this operation
The code to do the above looks like this:
QUnit.test("inserting a node having the same package as the node it is assigned to",
function() {
// create the base node
var node = SheetDataNode("example", "main.go", 3, 1)
// insert an other node, with identical package name
var otherNode = SheetDataNode(node.getPackageName(), "logUtility.go", 12, 3)
node.insertNode(otherNode)
// node should contain two files, and neither a left child nor a right child
deepEqual(node.getFiles().map(function(val) {
return val.name
}),
["logUtility.go", "main.go"],
"node contains the right file names")
equal(node.getFiles().length, 2, "A package got added to the node")
ok(!node.getLeftChild(), "leftChild still unset")
ok(!node.getRightChild(), "rightChild still unset")
})
Here is screenshot of the failing assertions:
Remember that the method under test is like this:
insertNode : function(node) {
// set the current node as the head node
var currentNode = this;
// while we are on a non-null node
while (currentNode) {
// if the package of node is the same as that of currentNode
if (currentNode.getPackageName() === node.getPackageName()) {
// simply add the files of node to currentNode._files
currentNode.addFiles(node.getFiles())
return
}
// if the package of node "comes before" that of currentNode, move to the left
if (currentNode.getPackageName() > node.getPackageName()) {
// if the left child of node is defined, that becomes the current node
if (currentNode.getLeftChild()) currentNode = currentNode.getLeftChild()
// else construct it, and we're done
else {
currentNode.setLeftChild(node)
return
}
}
// if the package of node "comes after" that of currentNode, move to the right
if (currentNode.getPackageName() < node.getPackageName()) {
// if the right child of node is defined, that becomes the current node
if (currentNode.getRightChild()) currentNode = currentNode.getRightChild()
// else construct it, and we're done
else {
currentNode.setRightChild(node)
return
}
}
throw Error("Whoa, some infinite looping was about to happen!")
}
The test against the method addFiles, which has this code:
QUnit.test("testing method addFiles",
function() {
// create the base node
var node = SheetDataNode("example", "main.go", 3, 1)
// create an array of files to add
const filesToAdd = [{
name : 'aFile.go',
complexity : 10
}, {
name : 'anotherFile.go',
complexity : 10
}, {
name : 'yetAnotherFile.go',
complexity : 10
}]
// is node.getFiles() an array?!
ok(Array.isArray(node.getFiles()), "node.getFiles() is an array")
// add the files
node.addFiles(filesToAdd)
Logger.log(node.getFiles())
// node.getFiles() should be an Array
ok(Array.isArray(node.getFiles()), "node.getFiles() is still an array")
// node.getFiles should now contain filesToAdd
equal(node.getFiles().length, 1 + filesToAdd.length, "node.getFiles().length increased by the length of the files to add")
})
passes:
, as do the other tests against insertNode, meaning the problem might exist with how we try to reference currentNode in insertNode for array property modification. If so, I have no idea how else to reference, in Google Apps Script, the SheetDataNode to undergo state change
I was able to solve the problem, with inspiration from the MDN docs on closures, by changing the private function property declaration from :
_addFile = function(file) {
for (var f in _files) {
if (file.name < _files[f].name) {
_files.splice(f, 0, file)
return
}
}
_files.push(file)
}
to
function _addFile(file) {
for (var f in _files) {
if (file.name < _files[f].name) {
_files.splice(f, 0, file)
return
}
}
_files.push(file)
}
idk why this works, because I forgot the difference between declaring method like a function variable (what I was doing), and preceding the name of the method with function like it's any other function. I'll have to (re-)learn that...
I have the code to find the kth from the last element in a list in golang. I wrote a recursive function. When it reaches the end of the list, it will return the count as 1 and increments in further returns. When the count == k then return the node value. But I am getting 'nil pointer dereference' error. Could anyone help me in this?
package main
import (
"container/list"
"fmt"
)
var sMap map[int]bool
func main() {
l := list.New()
for i := 1; i < 100; i++ {
l.PushBack(i)
}
kFromLastElemRec := findKFromLastRecr(l.Front(), 3, WrapObj{0})
fmt.Println(kFromLastElemRec.Value.(int))
}
//Object to store the count
type WrapObj struct {
count int
}
//ERROR
//recursive function to find the kth from last element
func findKFromLastRecr(head *list.Element, k int, wrapper WrapObj) *list.Element {
if head == nil {
return nil
}
resNode := findKFromLastRecr(head.Next(), k, wrapper)
wrapper.count = (wrapper.count) + 1
if wrapper.count == k {
return head
}
return resNode
}
You need to pass a pointer to WrapObj to the findKFromLastRecr() function.
Similar to the languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter.
For instance, passing a WrapObj value to a function makes a copy of the WrapObj, but not the WrapObj it points to.
Therefore, without a pointer to WrapObj, each findKFromLastRecr() function gets a copy of WrapObj and the increase can not be shared by the outer findKFromLastRecr() functions.
It could be useful to check the pointers section from golang-book.
package main
import (
"container/list"
"fmt"
)
var sMap map[int]bool
func main() {
l := list.New()
for i := 1; i < 100; i++ {
l.PushBack(i)
}
kFromLastElemRec := findKFromLastRecr(l.Front(), 3, &WrapObj{0})
fmt.Println(kFromLastElemRec.Value.(int))
}
//Object to store the count
type WrapObj struct {
count int
}
//ERROR
//recursive function to find the kth from last element
func findKFromLastRecr(head *list.Element, k int, wrapper *WrapObj) *list.Element {
if head == nil {
return nil
}
resNode := findKFromLastRecr(head.Next(), k, wrapper)
wrapper.count = (wrapper.count) + 1
if wrapper.count == k {
return head
}
return resNode
}
Output
97