complex data structure - insert value

S

spar

I'm converting a Perl script to Python and have run into something I'm
not sure how to do in Python.

In Perl, I am running through a couple loops and inserting values
directly into a complex data structure (array->hash->array). This
isn't the actual code, but should demonstrate the general idea:

foreach $bar_count(@bars) {
foreach $el_count(@els) {
$var = somefunc($bar_count,$el_count);
$data[$bar_count]->{'CC'}->[$el_count] = $var;
}
}

Basically I'd like to do the same thing in Python. I've written a
convoluted test script where I can print an element:

print data[0]['CC'][1]

but I can't insert directly like I do in Perl (data[0]['CC'][1] =
$var). In my test script I've simply initialized my lists and
dictionary at the beginning of the script, not by iterating an
inserting values as in the Perl example.

Any advice appreciated...thanks.
 
J

Jeffrey Froman

spar said:
Basically I'd like to do the same thing in Python. I've written a
convoluted test script where I can print an element:

print data[0]['CC'][1]

but I can't insert directly like I do in Perl (data[0]['CC'][1] =
$var).

This should work fine, though in python this would result in assignment, not
insertion:

***************************
#Assignment:
x=[{'foo':[1,2,3]}]
x[0]['foo'][1] = 'bar'
x
[{'foo': [1, 'bar', 3]}]
#Insertion:
x[0]['foo'].insert(2, 'baz')
x
[{'foo': [1, 'bar', 'baz', 3]}]

***************************

Jeffrey
 
N

Nick Craig-Wood

Jeffrey Froman said:
spar said:
Basically I'd like to do the same thing in Python. I've written a
convoluted test script where I can print an element:

print data[0]['CC'][1]

but I can't insert directly like I do in Perl (data[0]['CC'][1] =
$var).

This should work fine, though in python this would result in assignment, not
insertion:
#Assignment:
x=[{'foo':[1,2,3]}]
x[0]['foo'][1] = 'bar'
x
[{'foo': [1, 'bar', 3]}]
#Insertion:
x[0]['foo'].insert(2, 'baz')
x
[{'foo': [1, 'bar', 'baz', 3]}]

The OP is relying on a perl feature called auto-vivification. In perl
speak when you do

$data->[0]->{'CC'}->[1] = "spam";

The only thing that has to exist is the array reference in $data.
Perl will then auto-vivify the hash then the second arrayref.

Python doesn't do this in its standard list and dict objects. So
either you create some subclasses which do (which isn't that hard -
need to overload __setitem__ in both cases and do the perl thing).

Alternatively you can do it the python way... The original

foreach $bar_count(@bars) {
foreach $el_count(@els) {
$var = somefunc($bar_count,$el_count);
$data[$bar_count]->{'CC'}->[$el_count] = $var;
}
}

becomes

def somefunc(a,b): return 1
bars=[1, 2, 3, 4, 5]
els=[7, 8]

data = []
for bar_count in bars:
for el_count in els:
var = somefunc(bar_count, el_count)
while bar_count >= len(data):
data.append({})
l = data[bar_count].setdefault('CC', [])
while el_count >= len(l):
l.append(None)
l[el_count] = var

[{},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]}]


Or you may prefer this which you can't do in perl (because keys of
hashes can only be strings)

data = {}
for bar_count in bars:
for el_count in els:
var = somefunc(bar_count, el_count)
data[(bar_count,'CC',el_count)] = var
{(1, 'CC', 7): 1,
(1, 'CC', 8): 1,
(2, 'CC', 7): 1,
(2, 'CC', 8): 1,
(3, 'CC', 7): 1,
(3, 'CC', 8): 1,
(4, 'CC', 7): 1,
(4, 'CC', 8): 1,
(5, 'CC', 7): 1,
(5, 'CC', 8): 1}

It does't produce quite the same data structures but it may be useful
for you.

However one thing I've noticed from converting quite a few perl
programs to python is that constant 'CC' in a hash is indicative of
the fact that you really should be defining a class, of which CC
becomes an attribute or method. In perl the way is to define complex
data structures of hashes and lists, but in python because its so much
easier to create a class when you want it, do that.

So whenever you see $hash->{constant_string} you should be thinking -
hmm shouldn't that be a class. And when you've made it a class you'll
find that you have functions which should be methods of that class,
and before you know it you'll find your program looks a lot tidier!
 
S

spar

Nick Craig-Wood said:
def somefunc(a,b): return 1
bars=[1, 2, 3, 4, 5]
els=[7, 8]

data = []
for bar_count in bars:
for el_count in els:
var = somefunc(bar_count, el_count)
while bar_count >= len(data):
data.append({})
l = data[bar_count].setdefault('CC', [])
while el_count >= len(l):
l.append(None)
l[el_count] = var

[{},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]},
{'CC': [None, None, None, None, None, None, None, 1, 1]}]


Thanks, this is close...I'll have to play with it a bit. I've done
Perl for quite sometime, but am new to Python.

My example code didn't exactly show what I'm trying to do. The data
structure should actually look something like this (think of each
array element as a musical bar, each dict element ('HH', etc.) is an
instrument in that bar, and the list within the dict represent beats
for that instrument within the bar):

[{},
{'CC': ['-', '-', '-', '-'], 'HH': ['x', 'x', 'x', 'x'], 'SD': ['-',
'-', 'o', '-']},
{'CC': ['x', '-', '-', '-'], 'HH': ['x', 'x', 'x', 'x'], 'SD': ['o',
'-', 'o', '-']},
{'CC': ['-', '-', '-', '-'], 'HH': ['x', 'x', 'x', 'x'], 'SD': ['-',
'-', 'o', '-']},
{'CC': ['x', '-', '-', '-'], 'HH': ['x', 'x', 'x', 'x'], 'SD': ['o',
'-', 'o', '-']}]

So this shows bars 1-4 (there is no bar 0, so it's empty), each bar
has three instruments ('CC', 'HH', and 'SD') and each instrument shows
what is to be played on beats (subdivisions) 1-4 (actually there will
be 16 subdivisions representing 16 16th notes).

I think I can take your code and mold it into what I need. Thanks...
 
A

Alex Martelli

spar said:
foreach $bar_count(@bars) {
foreach $el_count(@els) {
$var = somefunc($bar_count,$el_count);
$data[$bar_count]->{'CC'}->[$el_count] = $var;
}
}

Not quite sure what you mean, but maybe s/thing like...:

data = {}

for bar_count in bars:
for el_count in els:
var = somefunc(bar_count, el_count)
subdict = data.set_default(bar_count, {})
subdict = subdict.set_default('CC', {})
subdict[el_count] = var

....?


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top