0

I'm having difficulty adding to a list iteratively.

Here's a MWE:

# Given a nested list of values, or sets
sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]
    
# add a value to each sublist giving the number of that set in the list.
n_sets = len(sets)
for s in range(n_sets):
    (sets[s]).insert(0, s)

# Now repeat those sets reps times
reps = 4
expanded_sets = [item for item in sets for i in range(reps)]

# then assign a repetition number to each occurance of a set.
rep_list = list(range(reps)) * n_sets
for i in range(n_sets * reps):
    (expanded_sets[i]).insert(0, rep_list[i])
    
expanded_sets

which returns

[[3, 2, 1, 0, 0, 1, 2, 3],
 [3, 2, 1, 0, 0, 1, 2, 3],
 [3, 2, 1, 0, 0, 1, 2, 3],
 [3, 2, 1, 0, 0, 1, 2, 3],
 [3, 2, 1, 0, 1, 1, 2, 4],
 [3, 2, 1, 0, 1, 1, 2, 4],
 [3, 2, 1, 0, 1, 1, 2, 4],
 [3, 2, 1, 0, 1, 1, 2, 4],
 [3, 2, 1, 0, 2, 1, 2, 5],
 [3, 2, 1, 0, 2, 1, 2, 5],
 [3, 2, 1, 0, 2, 1, 2, 5],
 [3, 2, 1, 0, 2, 1, 2, 5]]

instead of the desired

[[0, 0, 1, 2, 3],
 [1, 0, 1, 2, 3],
 [2, 0, 1, 2, 3],
 [3, 0, 1, 2, 3],
 [0, 1, 1, 2, 4],
 [1, 1, 1, 2, 4],
 [2, 1, 1, 2, 4],
 [3, 1, 1, 2, 4],
 [0, 2, 1, 2, 5],
 [1, 2, 1, 2, 5],
 [2, 2, 1, 2, 5],
 [3, 2, 1, 2, 5]]

Just for fun, the first loop returns an expected value of sets

[[0, 1, 2, 3], [1, 1, 2, 4], [2, 1, 2, 5]]

but after the second loop sets changed to

[[3, 2, 1, 0, 0, 1, 2, 3], [3, 2, 1, 0, 1, 1, 2, 4], [3, 2, 1, 0, 2, 1, 2, 5]]

I suspect the issue has something to do with copies and references. I've tried adding .copy() and slices in various places, but with the indexed sublists I haven't come across a combo that works. I'm running Python 3.10.6.

Thanks for looking!

Per suggested solution, [list(range(reps)) for _ in range(n_sets)] doesn't correctly replace the list(range(reps)) * n_sets, since it gives [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] instead of the desired [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]. Do I need to flatten, or is there a syntax with the _ notation that gives me a single list?

Further update . . . replacing

rep_list = list(range(reps)) * n_sets

with

rep_list_nest = [list(range(reps)) for _ in range(n_sets)]
rep_list = [i for sublist in rep_list_nest for i in sublist]

gives the same undesired result for expanded_sets.

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
zazizoma
  • 437
  • 1
  • 7
  • 18
  • Does this answer your question? [List of lists changes reflected across sublists unexpectedly](https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly) – Pranav Hosangadi Feb 08 '23 at 04:31
  • Maybe, this means the problem is in the * n_sets instead of the inserts, which it where I thought is was. Not clear how to apply the _ notation from the one you linked to this, though. – zazizoma Feb 08 '23 at 04:36
  • 1
    After further exploration, no it does not, though it may be a piece of the solution. – zazizoma Feb 08 '23 at 05:13

1 Answers1

1

The problem is here:

expanded_sets = [item 
                 for item in sets
                 for i in range(reps)]

This list now contains the same element of sets four times in a row, followed by the next element repeated four times, and so on.

Creating copies of item fixes the issue:

expanded_sets = [item.copy() 
                 for item in sets
                 for i in range(reps)]

Try it online

If you want a more pythonic approach, then recognize that the result is a product of two ranges, and your original sets all concatenated together:

from itertools import product

sets = [[1, 2, 3], [1, 2, 4], [1, 2, 5]]

expanded_sets = [[inner_counter, outer_counter] + sets_elem
                  for sets_elem, outer_counter, inner_counter in product(sets, range(len(sets)), range(4))]

Try it online

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
  • Oh excellent, thank you. That makes sense. I knew I needed a copy somewhere but couldn't figure out the precise location. – zazizoma Feb 08 '23 at 12:40