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

Backend Question Pull Request #6

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions answer.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT projects.project_name as Project, SUM(employees.salary) as Total, AVG(employees.age) as "Avg"
FROM employees
JOIN employee_projects on employees.id = employee_projects.employee_id
JOIN projects on projects.project_id = employee_projects.project_id
GROUP BY projects.project_id;

/* I would index employees.id, employee_projects.employee_id, employee_projects.project_id,
and project.project_id
*/
138 changes: 138 additions & 0 deletions math_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/python

from numbers import Number


class InvalidOperator(Exception):
""" An invalid operator was passed """
pass


class InvalidNode(Exception):
""" The node was created with no value """
pass


class InvalidNumber(Exception):
""" The node was not passed a number """
pass


class InvalidTree(Exception):
""" The tree dictionary didn't have the proper structure """
pass


class NoChildren(Exception):
""" Tried to calculate node with no children """
pass


class Node(object):
"""
Object with basic Node properties that will be extended with OperatorNode and NumberNode.
Can contain any arbitrary number of Nodes.
"""
def __init__(self, val):
if val == "":
raise InvalidNode("Node cannot be empty")
self.val = val
self.children = []

def add_child(self, child):
self.children.append(child)

def __str__(self):
return str(self.val)


class OperatorNode(Node):
"""
OperatorNode has a value of +, -, /, or *
calculate() will return the value of all nodes under the root node.
"""
operators = ["+", "-", "/", "*"]

def __init__(self, val):
if val not in self.operators:
raise InvalidOperator("Invalid Operator. Must be in set(+ - / *)")
self._operator = val
super(OperatorNode, self).__init__(val)

@property
def operator(self):
return self._operator

def calculate(self):
"""
Recursively walk through all children in OperatorNode and solve until only
a NumberNode is left.
"""
if len(self.children) < 2:
raise NoChildren("Must have at least two children NumberNodes to calculate")

def calc_children(children, operator):
number_nodes = []
for node in children:
if isinstance(node, OperatorNode):
number_nodes.append(calc_children(node.children, node.operator))
else:
number_nodes.append(node)
return self.solve_expression(number_nodes, operator)
return calc_children(self.children, self.operator)

def solve_expression(self, number_nodes, operator):
"""
Create an expression to calculate with eval()
"""
expr = (" %s " % operator).join([str(float(n)) for n in number_nodes])
result = eval(expr)
return NumberNode(result)


class NumberNode(Node):
"""
A node that stores a number instead of an operator.
"""
def __init__(self, val):
if not isinstance(val, Number):
raise InvalidNumber("Value must be a number")
super(NumberNode, self).__init__(val)

def __int__(self):
return int(self.val)

def __float__(self):
return float(self.val)


class Tree(OperatorNode):
"""
Create a Tree object from a dictionary.
"""
def __init__(self, tree_dict):
if len(tree_dict.keys()) > 1:
raise InvalidTree("Tree can only have one root node")
super(Tree, self).__init__(tree_dict.keys()[0])
for node in self.tree(tree_dict).children:
self.add_child(node)

def tree(self, tree):
def walk_tree(new_tree):
op_node = OperatorNode(new_tree.keys()[0])
for item in new_tree[new_tree.keys()[0]]:
if isinstance(item, dict):
op_node.add_child(walk_tree(item))
else:
op_node.add_child(NumberNode(item))
return op_node

root = tree.keys()[0]
root_node = OperatorNode(root)
for child in tree[root]:
if isinstance(child, dict):
root_node.add_child(walk_tree(child))
else:
root_node.add_child(NumberNode(child))

return root_node
233 changes: 233 additions & 0 deletions test_math_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#!/usr/bin/python

from math_tree import *

import unittest


class TreeGraph(unittest.TestCase):
def test_create_node(self):
node = Node("test")
self.assertEquals(node.val, "test")

def test_create_node_exception(self):
self.assertRaises(InvalidNode, Node, "")

def test_create_operator_node(self):
node = OperatorNode("+")
self.assertEquals("+", node.val)

def test_create_op_node_exception(self):
self.assertRaises(InvalidOperator, OperatorNode, "A")

def test_create_number_node(self):
node = NumberNode(2)
self.assertEquals(node.val, 2)

def test_create_number_node_exception(self):
self.assertRaises(InvalidNumber, NumberNode, "A")

def test_calculate_node_no_children(self):
node = OperatorNode("+")
self.assertRaises(NoChildren, node.calculate)

def test_calculate_node_one_child(self):
node = OperatorNode("+")
node.add_child(NumberNode(2))
self.assertRaises(NoChildren, node.calculate)

def test_calculate_tree1(self):
"""
+
/ \
3 -
/ \
+ 5
/ \
4 10

answer: 12

"""
root_node = OperatorNode("+")
a_node = NumberNode(3)
b_node = OperatorNode("-")
c_node = OperatorNode("+")
d_node = NumberNode(5)
e_node = NumberNode(4)
f_node = NumberNode(10)
root_node.add_child(a_node)
root_node.add_child(b_node)
b_node.add_child(c_node)
b_node.add_child(d_node)
c_node.add_child(e_node)
c_node.add_child(f_node)

self.assertEquals(float(root_node.calculate()), float(12))

def test_calculate_tree2(self):
"""
+
/ \
3 - 3 + 10 = 13
/ \
+ 5 15 - 5 = 10
/ \
- 10 5 + 10 = 15
/ \
9 + 9 - 4 = 5
/ \
- 6 -2 + 6 = 4
/ \
2 4 2 - 4 = -2

"""
root_node = OperatorNode("+")
a = NumberNode(3)
b = OperatorNode("-")
c = OperatorNode("+")
d = NumberNode(5)
e = OperatorNode("-")
f = NumberNode(10)
g = NumberNode(9)
h = OperatorNode("+")
i = OperatorNode("-")
j = NumberNode(6)
k = NumberNode(2)
l = NumberNode(4)

root_node.add_child(a)
root_node.add_child(b)
b.add_child(c)
b.add_child(d)
c.add_child(e)
c.add_child(f)
e.add_child(g)
e.add_child(h)
h.add_child(i)
h.add_child(j)
i.add_child(k)
i.add_child(l)

self.assertEquals(float(root_node.calculate()), float(13))

def test_calculate_tree3(self):
"""
-
/ \
3 * 3 - 150 = -147
/ \
* 5 30 * 5 = 150
/ \
(div) 10 3 * 10 = 30
/ \
9 3 9 / 3 = 3

"""
root_node = OperatorNode("-")
a = NumberNode(3)
b = OperatorNode("*")
c = OperatorNode("*")
d = NumberNode(5)
e = OperatorNode("/")
f = NumberNode(10)
g = NumberNode(9)
h = NumberNode(3)

root_node.add_child(a)
root_node.add_child(b)
b.add_child(c)
b.add_child(d)
c.add_child(e)
c.add_child(f)
e.add_child(g)
e.add_child(h)

self.assertEquals(float(root_node.calculate()), float(-147))

def test_calculate_tree4(self):
"""
(div)
/ \
3 * 3 / 150 = -147
/ \
* 5 30 * 5 = 150
/ \
(div) 10 3 * 10 = 30
/ \
9 3 9 / 3 = 3

"""
root_node = OperatorNode("/")
a = NumberNode(3)
b = OperatorNode("*")
c = OperatorNode("*")
d = NumberNode(5)
e = OperatorNode("/")
f = NumberNode(10)
g = NumberNode(9)
h = NumberNode(3)

root_node.add_child(a)
root_node.add_child(b)
b.add_child(c)
b.add_child(d)
c.add_child(e)
c.add_child(f)
e.add_child(g)
e.add_child(h)

self.assertEquals(float(root_node.calculate()), 0.02)

def test_calculate_tree5(self):
"""
+
/ | \
3 - 3
/ \
+ 5
/ / \
5 4 10

answer: 20

"""
root_node = OperatorNode("+")
a_node = NumberNode(3)
b_node = OperatorNode("-")
c_node = OperatorNode("+")
d_node = NumberNode(5)
e_node = NumberNode(4)
f_node = NumberNode(10)
g_node = NumberNode(3)
h_node = NumberNode(5)
root_node.add_child(a_node)
root_node.add_child(b_node)
root_node.add_child(g_node)
b_node.add_child(c_node)
b_node.add_child(d_node)
c_node.add_child(e_node)
c_node.add_child(f_node)
c_node.add_child(h_node)

self.assertEquals(float(root_node.calculate()), float(20))

def test_create_tree_from_dict(self):
"""
+
/ \
2 -
/ \
4 +
/ \
- *
/ \ / \
7 8 9 5
"""

tree_dict = {"+": [2, {"-": [4, {"+": [{"-": [7, 8]}, {"*": [9, 5]}]}]}]}
tree = Tree(tree_dict)
self.assertEquals(float(tree.calculate()), float(-38))

if __name__ == '__main__':
unittest.main()