Data structure for arrays which share some elements — Python
Clash Royale CLAN TAG#URR8PPP
up vote
14
down vote
favorite
I have a collection of arrays which "overlap" at certain elements. Here's a picture of an example involving 3 arrays of characters:
array0↓
'A' ↓array2
array1→'B' 'D' 'E'
'C' 'F'
The important thing is that changes to the arrays should respect this structure. So for example, if I change the 'B' in array0 to 'X', the 'B' in array1 should also change to 'X'.
My question is what is a good, efficient way of implementing this in Python?
There are two things I have thought of so far:
One, I can make a bespoke class, instances of which contain a completely distinct list, along with information about any overlaps that it has, and implement update methods appropriately so that any change to the list is always duplicated for the other lists at the overlaps. This seems a little overwrought though, and involves duplicating the data.
Two, I could do it by using singleton lists like this:
data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]
for array in array0, array1, array2:
print(array)
>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]
array0[1][0] = 'X'
for array in array0, array1, array2:
print(array)
>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]
But I feel this may be hacky and not the best way. Thanks for any suggestions.
python data-structures
add a comment |Â
up vote
14
down vote
favorite
I have a collection of arrays which "overlap" at certain elements. Here's a picture of an example involving 3 arrays of characters:
array0↓
'A' ↓array2
array1→'B' 'D' 'E'
'C' 'F'
The important thing is that changes to the arrays should respect this structure. So for example, if I change the 'B' in array0 to 'X', the 'B' in array1 should also change to 'X'.
My question is what is a good, efficient way of implementing this in Python?
There are two things I have thought of so far:
One, I can make a bespoke class, instances of which contain a completely distinct list, along with information about any overlaps that it has, and implement update methods appropriately so that any change to the list is always duplicated for the other lists at the overlaps. This seems a little overwrought though, and involves duplicating the data.
Two, I could do it by using singleton lists like this:
data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]
for array in array0, array1, array2:
print(array)
>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]
array0[1][0] = 'X'
for array in array0, array1, array2:
print(array)
>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]
But I feel this may be hacky and not the best way. Thanks for any suggestions.
python data-structures
1
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36
add a comment |Â
up vote
14
down vote
favorite
up vote
14
down vote
favorite
I have a collection of arrays which "overlap" at certain elements. Here's a picture of an example involving 3 arrays of characters:
array0↓
'A' ↓array2
array1→'B' 'D' 'E'
'C' 'F'
The important thing is that changes to the arrays should respect this structure. So for example, if I change the 'B' in array0 to 'X', the 'B' in array1 should also change to 'X'.
My question is what is a good, efficient way of implementing this in Python?
There are two things I have thought of so far:
One, I can make a bespoke class, instances of which contain a completely distinct list, along with information about any overlaps that it has, and implement update methods appropriately so that any change to the list is always duplicated for the other lists at the overlaps. This seems a little overwrought though, and involves duplicating the data.
Two, I could do it by using singleton lists like this:
data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]
for array in array0, array1, array2:
print(array)
>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]
array0[1][0] = 'X'
for array in array0, array1, array2:
print(array)
>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]
But I feel this may be hacky and not the best way. Thanks for any suggestions.
python data-structures
I have a collection of arrays which "overlap" at certain elements. Here's a picture of an example involving 3 arrays of characters:
array0↓
'A' ↓array2
array1→'B' 'D' 'E'
'C' 'F'
The important thing is that changes to the arrays should respect this structure. So for example, if I change the 'B' in array0 to 'X', the 'B' in array1 should also change to 'X'.
My question is what is a good, efficient way of implementing this in Python?
There are two things I have thought of so far:
One, I can make a bespoke class, instances of which contain a completely distinct list, along with information about any overlaps that it has, and implement update methods appropriately so that any change to the list is always duplicated for the other lists at the overlaps. This seems a little overwrought though, and involves duplicating the data.
Two, I could do it by using singleton lists like this:
data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]
for array in array0, array1, array2:
print(array)
>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]
array0[1][0] = 'X'
for array in array0, array1, array2:
print(array)
>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]
But I feel this may be hacky and not the best way. Thanks for any suggestions.
python data-structures
asked Aug 24 at 13:54


Denziloe
1,958218
1,958218
1
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36
add a comment |Â
1
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36
1
1
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36
add a comment |Â
5 Answers
5
active
oldest
votes
up vote
2
down vote
Mine suggestion is variation of the one proposed by @a_guest. You could have a wrapper class that marks the elements as shared and a data structure for handling such elements:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement(0)".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
Output
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
I'd personally avoid mixing strings andSharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is astr
or aShredElement
and handle the two cases separately. I'd simply renameSharedElement
toElement
, and assign all values asElement
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.
– Bakuriu
Aug 24 at 17:48
UsingElement
for all values could also allow to share the state without having to reassign the values. For example ifx
andy
are instances that appear somewhere in thea
,b
,c
lists you can make them equal and shared by doing something likex.__dict__ = y.__dict__
without having to do something likec[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.
– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by aSharedElement
instance before I can use it. Sharing on demand circumvents this issue.
– a_guest
Aug 24 at 22:11
add a comment |Â
up vote
1
down vote
You can create a wrapper class that can handle the updating of all elements of the same value:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
Output:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
add a comment |Â
up vote
1
down vote
You could subclass list
and use a dedicated wrapper class to proxy shared content. This involves no data duplication as it only stores the proxy for shared data which dispatches to the original data. It's a bit similar to your nested list approach but it maintains the normal list interface. Here is an example implementation:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
Now you can share the data among your lists as required:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
add a comment |Â
up vote
1
down vote
You could use a dedicated class that updates other intersecting instances appropriately as you indicated with your first idea. I wouldn't consider data duplication a problem as for mutable data you anyway store the references and in case your using large immutable data you can employ a dedicated wrapper class (e.g. Python 3.7 introduced the @dataclass
decorator).
Here is an example implementation:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
With that you can intersect the lists as in your example:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
There doesn't seem to be much benefit to wrapping 2 of the 3intersect
args in an extra container, but other than that, it's a formidable solution
– Felix Dombek
Aug 24 at 18:42
add a comment |Â
up vote
0
down vote
As you pointed out in your question, the relevant information is that
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(I am adding the suffix ptr because practically those elements are pointers).
Here the list element are the pointer to the objects that shall be maintained
in a separate list
ol = ['A', 'B', 'C', 'D', 'E']
The real arrays can be obtained at run time by member functions like
array0 =
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
Now your point is : suppose that the object list becomes
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
How do I automagically keep track of this in my arrays?? Those arrays should become:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
I think that the most simple answer is : keep the list fixed!, and
do not allow inserting or changing the order of items. Simply mantain
a different hash with the object position. In the case above, you'll have
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
it is then possible to write member functions that dump the updated list of objects,
the array would not change. What can be tricky is if you want to delete objects, but this is tricky in any case I fear.
add a comment |Â
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
Mine suggestion is variation of the one proposed by @a_guest. You could have a wrapper class that marks the elements as shared and a data structure for handling such elements:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement(0)".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
Output
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
I'd personally avoid mixing strings andSharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is astr
or aShredElement
and handle the two cases separately. I'd simply renameSharedElement
toElement
, and assign all values asElement
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.
– Bakuriu
Aug 24 at 17:48
UsingElement
for all values could also allow to share the state without having to reassign the values. For example ifx
andy
are instances that appear somewhere in thea
,b
,c
lists you can make them equal and shared by doing something likex.__dict__ = y.__dict__
without having to do something likec[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.
– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by aSharedElement
instance before I can use it. Sharing on demand circumvents this issue.
– a_guest
Aug 24 at 22:11
add a comment |Â
up vote
2
down vote
Mine suggestion is variation of the one proposed by @a_guest. You could have a wrapper class that marks the elements as shared and a data structure for handling such elements:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement(0)".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
Output
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
I'd personally avoid mixing strings andSharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is astr
or aShredElement
and handle the two cases separately. I'd simply renameSharedElement
toElement
, and assign all values asElement
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.
– Bakuriu
Aug 24 at 17:48
UsingElement
for all values could also allow to share the state without having to reassign the values. For example ifx
andy
are instances that appear somewhere in thea
,b
,c
lists you can make them equal and shared by doing something likex.__dict__ = y.__dict__
without having to do something likec[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.
– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by aSharedElement
instance before I can use it. Sharing on demand circumvents this issue.
– a_guest
Aug 24 at 22:11
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Mine suggestion is variation of the one proposed by @a_guest. You could have a wrapper class that marks the elements as shared and a data structure for handling such elements:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement(0)".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
Output
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
Mine suggestion is variation of the one proposed by @a_guest. You could have a wrapper class that marks the elements as shared and a data structure for handling such elements:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement(0)".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
Output
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
answered Aug 24 at 16:34


Daniel Mesejo
2,058513
2,058513
I'd personally avoid mixing strings andSharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is astr
or aShredElement
and handle the two cases separately. I'd simply renameSharedElement
toElement
, and assign all values asElement
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.
– Bakuriu
Aug 24 at 17:48
UsingElement
for all values could also allow to share the state without having to reassign the values. For example ifx
andy
are instances that appear somewhere in thea
,b
,c
lists you can make them equal and shared by doing something likex.__dict__ = y.__dict__
without having to do something likec[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.
– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by aSharedElement
instance before I can use it. Sharing on demand circumvents this issue.
– a_guest
Aug 24 at 22:11
add a comment |Â
I'd personally avoid mixing strings andSharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is astr
or aShredElement
and handle the two cases separately. I'd simply renameSharedElement
toElement
, and assign all values asElement
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.
– Bakuriu
Aug 24 at 17:48
UsingElement
for all values could also allow to share the state without having to reassign the values. For example ifx
andy
are instances that appear somewhere in thea
,b
,c
lists you can make them equal and shared by doing something likex.__dict__ = y.__dict__
without having to do something likec[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.
– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by aSharedElement
instance before I can use it. Sharing on demand circumvents this issue.
– a_guest
Aug 24 at 22:11
I'd personally avoid mixing strings and
SharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is a str
or a ShredElement
and handle the two cases separately. I'd simply rename SharedElement
to Element
, and assign all values as Element
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.– Bakuriu
Aug 24 at 17:48
I'd personally avoid mixing strings and
SharedElements
. If you ever want to do some operation on those arrays you'll have to check if each element is a str
or a ShredElement
and handle the two cases separately. I'd simply rename SharedElement
to Element
, and assign all values as Element
s but refer to the same instances if they have to be shared. In this way the handling will be uniform.– Bakuriu
Aug 24 at 17:48
Using
Element
for all values could also allow to share the state without having to reassign the values. For example if x
and y
are instances that appear somewhere in the a
, b
, c
lists you can make them equal and shared by doing something like x.__dict__ = y.__dict__
without having to do something like c[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.– Bakuriu
Aug 24 at 17:54
Using
Element
for all values could also allow to share the state without having to reassign the values. For example if x
and y
are instances that appear somewhere in the a
, b
, c
lists you can make them equal and shared by doing something like x.__dict__ = y.__dict__
without having to do something like c[c.index(x)] = y
. This is surely hackish but in some circumstance it might be the easiest solution.– Bakuriu
Aug 24 at 17:54
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by a
SharedElement
instance before I can use it. Sharing on demand circumvents this issue.– a_guest
Aug 24 at 22:11
I don't see the advantage of this approach as compared to making elements shared on demand (as depicted in this answer). That way you have to mark elements shared before you even know that you might share them later on. What if I want to share an element of an existing list? Then I need to replace it by a
SharedElement
instance before I can use it. Sharing on demand circumvents this issue.– a_guest
Aug 24 at 22:11
add a comment |Â
up vote
1
down vote
You can create a wrapper class that can handle the updating of all elements of the same value:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
Output:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
add a comment |Â
up vote
1
down vote
You can create a wrapper class that can handle the updating of all elements of the same value:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
Output:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
add a comment |Â
up vote
1
down vote
up vote
1
down vote
You can create a wrapper class that can handle the updating of all elements of the same value:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
Output:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
You can create a wrapper class that can handle the updating of all elements of the same value:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
Output:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
answered Aug 24 at 14:11


Ajax1234
35.8k42045
35.8k42045
add a comment |Â
add a comment |Â
up vote
1
down vote
You could subclass list
and use a dedicated wrapper class to proxy shared content. This involves no data duplication as it only stores the proxy for shared data which dispatches to the original data. It's a bit similar to your nested list approach but it maintains the normal list interface. Here is an example implementation:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
Now you can share the data among your lists as required:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
add a comment |Â
up vote
1
down vote
You could subclass list
and use a dedicated wrapper class to proxy shared content. This involves no data duplication as it only stores the proxy for shared data which dispatches to the original data. It's a bit similar to your nested list approach but it maintains the normal list interface. Here is an example implementation:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
Now you can share the data among your lists as required:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
add a comment |Â
up vote
1
down vote
up vote
1
down vote
You could subclass list
and use a dedicated wrapper class to proxy shared content. This involves no data duplication as it only stores the proxy for shared data which dispatches to the original data. It's a bit similar to your nested list approach but it maintains the normal list interface. Here is an example implementation:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
Now you can share the data among your lists as required:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
You could subclass list
and use a dedicated wrapper class to proxy shared content. This involves no data duplication as it only stores the proxy for shared data which dispatches to the original data. It's a bit similar to your nested list approach but it maintains the normal list interface. Here is an example implementation:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
Now you can share the data among your lists as required:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
answered Aug 24 at 15:47
a_guest
3,88911038
3,88911038
add a comment |Â
add a comment |Â
up vote
1
down vote
You could use a dedicated class that updates other intersecting instances appropriately as you indicated with your first idea. I wouldn't consider data duplication a problem as for mutable data you anyway store the references and in case your using large immutable data you can employ a dedicated wrapper class (e.g. Python 3.7 introduced the @dataclass
decorator).
Here is an example implementation:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
With that you can intersect the lists as in your example:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
There doesn't seem to be much benefit to wrapping 2 of the 3intersect
args in an extra container, but other than that, it's a formidable solution
– Felix Dombek
Aug 24 at 18:42
add a comment |Â
up vote
1
down vote
You could use a dedicated class that updates other intersecting instances appropriately as you indicated with your first idea. I wouldn't consider data duplication a problem as for mutable data you anyway store the references and in case your using large immutable data you can employ a dedicated wrapper class (e.g. Python 3.7 introduced the @dataclass
decorator).
Here is an example implementation:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
With that you can intersect the lists as in your example:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
There doesn't seem to be much benefit to wrapping 2 of the 3intersect
args in an extra container, but other than that, it's a formidable solution
– Felix Dombek
Aug 24 at 18:42
add a comment |Â
up vote
1
down vote
up vote
1
down vote
You could use a dedicated class that updates other intersecting instances appropriately as you indicated with your first idea. I wouldn't consider data duplication a problem as for mutable data you anyway store the references and in case your using large immutable data you can employ a dedicated wrapper class (e.g. Python 3.7 introduced the @dataclass
decorator).
Here is an example implementation:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
With that you can intersect the lists as in your example:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
You could use a dedicated class that updates other intersecting instances appropriately as you indicated with your first idea. I wouldn't consider data duplication a problem as for mutable data you anyway store the references and in case your using large immutable data you can employ a dedicated wrapper class (e.g. Python 3.7 introduced the @dataclass
decorator).
Here is an example implementation:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
With that you can intersect the lists as in your example:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
Which gives as output:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
edited Aug 24 at 15:48
answered Aug 24 at 14:25
a_guest
3,88911038
3,88911038
There doesn't seem to be much benefit to wrapping 2 of the 3intersect
args in an extra container, but other than that, it's a formidable solution
– Felix Dombek
Aug 24 at 18:42
add a comment |Â
There doesn't seem to be much benefit to wrapping 2 of the 3intersect
args in an extra container, but other than that, it's a formidable solution
– Felix Dombek
Aug 24 at 18:42
There doesn't seem to be much benefit to wrapping 2 of the 3
intersect
args in an extra container, but other than that, it's a formidable solution– Felix Dombek
Aug 24 at 18:42
There doesn't seem to be much benefit to wrapping 2 of the 3
intersect
args in an extra container, but other than that, it's a formidable solution– Felix Dombek
Aug 24 at 18:42
add a comment |Â
up vote
0
down vote
As you pointed out in your question, the relevant information is that
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(I am adding the suffix ptr because practically those elements are pointers).
Here the list element are the pointer to the objects that shall be maintained
in a separate list
ol = ['A', 'B', 'C', 'D', 'E']
The real arrays can be obtained at run time by member functions like
array0 =
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
Now your point is : suppose that the object list becomes
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
How do I automagically keep track of this in my arrays?? Those arrays should become:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
I think that the most simple answer is : keep the list fixed!, and
do not allow inserting or changing the order of items. Simply mantain
a different hash with the object position. In the case above, you'll have
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
it is then possible to write member functions that dump the updated list of objects,
the array would not change. What can be tricky is if you want to delete objects, but this is tricky in any case I fear.
add a comment |Â
up vote
0
down vote
As you pointed out in your question, the relevant information is that
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(I am adding the suffix ptr because practically those elements are pointers).
Here the list element are the pointer to the objects that shall be maintained
in a separate list
ol = ['A', 'B', 'C', 'D', 'E']
The real arrays can be obtained at run time by member functions like
array0 =
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
Now your point is : suppose that the object list becomes
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
How do I automagically keep track of this in my arrays?? Those arrays should become:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
I think that the most simple answer is : keep the list fixed!, and
do not allow inserting or changing the order of items. Simply mantain
a different hash with the object position. In the case above, you'll have
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
it is then possible to write member functions that dump the updated list of objects,
the array would not change. What can be tricky is if you want to delete objects, but this is tricky in any case I fear.
add a comment |Â
up vote
0
down vote
up vote
0
down vote
As you pointed out in your question, the relevant information is that
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(I am adding the suffix ptr because practically those elements are pointers).
Here the list element are the pointer to the objects that shall be maintained
in a separate list
ol = ['A', 'B', 'C', 'D', 'E']
The real arrays can be obtained at run time by member functions like
array0 =
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
Now your point is : suppose that the object list becomes
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
How do I automagically keep track of this in my arrays?? Those arrays should become:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
I think that the most simple answer is : keep the list fixed!, and
do not allow inserting or changing the order of items. Simply mantain
a different hash with the object position. In the case above, you'll have
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
it is then possible to write member functions that dump the updated list of objects,
the array would not change. What can be tricky is if you want to delete objects, but this is tricky in any case I fear.
As you pointed out in your question, the relevant information is that
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(I am adding the suffix ptr because practically those elements are pointers).
Here the list element are the pointer to the objects that shall be maintained
in a separate list
ol = ['A', 'B', 'C', 'D', 'E']
The real arrays can be obtained at run time by member functions like
array0 =
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
Now your point is : suppose that the object list becomes
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
How do I automagically keep track of this in my arrays?? Those arrays should become:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
I think that the most simple answer is : keep the list fixed!, and
do not allow inserting or changing the order of items. Simply mantain
a different hash with the object position. In the case above, you'll have
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
it is then possible to write member functions that dump the updated list of objects,
the array would not change. What can be tricky is if you want to delete objects, but this is tricky in any case I fear.
answered Aug 24 at 14:18
Lorenzo
12512
12512
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52005994%2fdata-structure-for-arrays-which-share-some-elements-python%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Do the overlaps have to match up with an actual crossword-style grid layout?
– user2357112
Aug 24 at 17:54
Good question, I considered addressing that. For the problem I'm actually working on that would be fine. And so you could use a 2 dimensional array to implement this -- although it could potentially have a lot of empty space. And I am curious to see solutions for the more general situation when this assumption isn't met (e.g. if we also paired 'C' and 'F' in the example).
– Denziloe
Aug 24 at 18:36