diff --git a/relational/relation.py b/relational/relation.py index a1396b7..c8a5f1e 100644 --- a/relational/relation.py +++ b/relational/relation.py @@ -20,7 +20,7 @@ # relational operations on them. import csv -from itertools import chain, repeat +from itertools import chain, repeat, product as iproduct from collections import deque from typing import * from pathlib import Path @@ -66,7 +66,7 @@ class Relation(NamedTuple): reader = csv.reader(fp) # Creating a csv reader header = Header(next(reader)) # read 1st line #FIXME load properly - content = frozenset((tuple(i) for i in reader)) + content = frozenset((tuple(Rstring(s) for s in i) for i in reader)) return Relation(header, content) def __iter__(self): @@ -111,14 +111,14 @@ class Relation(NamedTuple): ''' Selection, expr must be a valid Python expression; can contain field names. ''' - newt = Relation() - newt.header = Header(self.header) + header = Header(self.header) try: c_expr = compile(expr, 'selection', 'eval') except: raise Exception('Failed to compile expression: %s' % expr) + content = set() for i in self.content: # Fills the attributes dictionary with the values of the tuple attributes = {attr: i[j].autocast() @@ -127,11 +127,11 @@ class Relation(NamedTuple): try: if eval(c_expr, attributes): - newt.content.add(i) + content.add(i) except Exception as e: raise Exception( "Failed to evaluate %s\n%s" % (expr, e.__str__())) - return newt + return Relation(header, frozenset(content)) def product(self, other: 'Relation') -> 'Relation': ''' @@ -144,13 +144,10 @@ class Relation(NamedTuple): raise Exception( 'Unable to perform product on relations with colliding attributes' ) - newt = Relation() - newt.header = Header(self.header + other.header) + header = Header(self.header + other.header) - for i in self.content: - for j in other.content: - newt.content.add(i + j) - return newt + content = frozenset(i+j for i, j in iproduct(self.content, other.content)) + return Relation(header, content) def projection(self, *attributes) -> 'Relation': ''' @@ -172,16 +169,11 @@ class Relation(NamedTuple): if len(ids) == 0: raise Exception('Invalid attributes for projection') - newt = Relation() - # Create the header - h = (self.header[i] for i in ids) - newt.header = Header(h) + header = Header((self.header[i] for i in ids)) - # Create the body - for i in self.content: - row = (i[j] for j in ids) - newt.content.add(tuple(row)) - return newt + content = frozenset(tuple((i[j] for j in ids)) for i in self.content) + + return Relation(header, content) def rename(self, params: Dict[str, str]) -> 'Relation': ''' @@ -192,12 +184,8 @@ class Relation(NamedTuple): For example if you want to rename a to b, call rel.rename({'a':'b'}) ''' - newt = Relation() - newt.header = self.header.rename(params) - - newt.content = self.content - self._make_duplicate(newt) - return newt + header = self.header.rename(params) + return Relation(header, self.content) def intersection(self, other: 'Relation') -> 'Relation': ''' @@ -206,22 +194,14 @@ class Relation(NamedTuple): Will return an empty one if there are no common items. ''' other = self._rearrange(other) # Rearranges attributes' order - newt = Relation() - newt.header = Header(self.header) - - newt.content = self.content.intersection(other.content) - return newt + return Relation(self.header, self.content.intersection(other.content)) def difference(self, other: 'Relation') -> 'Relation': '''Difference operation. The result will contain items present in first operand but not in second one. ''' other = self._rearrange(other) # Rearranges attributes' order - newt = Relation() - newt.header = Header(self.header) - - newt.content = self.content.difference(other.content) - return newt + return Relation(self.header, self.content.difference(other.content)) def division(self, other: 'Relation') -> 'Relation': '''Division operator @@ -254,11 +234,7 @@ class Relation(NamedTuple): and second operands. ''' other = self._rearrange(other) # Rearranges attributes' order - newt = Relation() - newt.header = Header(self.header) - - newt.content = self.content.union(other.content) - return newt + return Relation(self.header, self.content.union(other.content)) def thetajoin(self, other: 'Relation', expr: str) -> 'Relation': '''Defined as product and then selection with the given expression.''' @@ -288,11 +264,10 @@ class Relation(NamedTuple): shared = self.header.intersection(other.header) - newt = Relation() # Creates the new relation # Creating the header with all the fields, done like that because order is # needed h = (i for i in other.header if i not in shared) - newt.header = Header(chain(self.header, h)) + header = Header(chain(self.header, h)) # Shared ids of self sid = self.header.getAttributesId(shared) @@ -302,6 +277,7 @@ class Relation(NamedTuple): # Non shared ids of the other relation noid = [i for i in range(len(other.header)) if i not in oid] + content = set() for i in self.content: # Tuple partecipated to the join? added = False @@ -313,14 +289,14 @@ class Relation(NamedTuple): if match: item = chain(i, (j[l] for l in noid)) - newt.content.add(tuple(item)) + content.add(tuple(item)) added = True # If it didn't partecipate, adds it if not added: item = chain(i, repeat(Rstring('---'), len(noid))) - newt.content.add(tuple(item)) + content.add(tuple(item)) - return newt + return Relation(header, frozenset(content)) def join(self, other: 'Relation') -> 'Relation': ''' @@ -331,12 +307,10 @@ class Relation(NamedTuple): # List of attributes in common between the relations shared = self.header.intersection(other.header) - newt = Relation() # Creates the new relation - # Creating the header with all the fields, done like that because order is # needed h = (i for i in other.header if i not in shared) - newt.header = Header(chain(self.header, h)) + header = Header(chain(self.header, h)) # Shared ids of self sid = self.header.getAttributesId(shared) @@ -346,6 +320,7 @@ class Relation(NamedTuple): # Non shared ids of the other relation noid = [i for i in range(len(other.header)) if i not in oid] + content = set() for i in self.content: for j in other.content: match = True @@ -354,9 +329,9 @@ class Relation(NamedTuple): if match: item = chain(i, (j[l] for l in noid)) - newt.content.add(tuple(item)) + content.add(tuple(item)) - return newt + return Relation(header, frozenset(content)) def __eq__(self, other): if not isinstance(other, Relation): @@ -410,7 +385,6 @@ class Relation(NamedTuple): Returns the number of affected rows. ''' - self._make_writable(copy_content=False) affected = self.selection(expr) not_affected = self.difference(affected) @@ -446,8 +420,6 @@ class Relation(NamedTuple): ) ) - self._make_writable() - prevlen = len(self.content) self.content.add(tuple(map(Rstring, values))) return len(self.content) - prevlen @@ -462,7 +434,6 @@ class Relation(NamedTuple): Returns the number of affected rows.''' l = len(self.content) - self._make_writable(copy_content=False) self.content = self.difference(self.selection(expr)).content return len(self.content) - l