# 5 Best Ways to Handle Python Lists of Lists with Different Lengths

💡 Problem Formulation: Dealing with a collection of lists within a single list where each inner list has a varying number of elements can present unique challenges. The goal is to understand how to effectively manage such structures, which are common in scenarios such as batch processing of data or generating matrix-like structures where rows have different lengths. For example, one might need to process input like `[[1, 2], [1, 2, 3, 4], [1]]` and be able to iterate, modify, or apply functions to these inner lists despite their variable sizes.

## Method 1: Iterating with a for Loop

In this method, you can simply use a nested `for` loop to iterate over each element in the list of lists, regardless of the inner lists’ lengths. This approach allows for straightforward access to each item and is best suited for operations that need to be applied to every single element individually.

Here’s an example:

```list_of_lists = [[1, 2], [1, 2, 3, 4], [1]]
for inner_list in list_of_lists:
for item in inner_list:
print(item)
```

The output of this code snippet:

```1
2
1
2
3
4
1
```

This code loops through each inner list and prints out its items individually, no matter how many items are there in each one. The double `for` loop structure makes this method highly versatile and easy to understand.

## Method 2: List Comprehension

List comprehensions offer a concise way to create or iterate over lists in Python. When working with a list of lists of different lengths, you can use a nested list comprehension to flatten the list or apply a function to each element. This method is succinct and often more readable than equivalent for loops.

Here’s an example:

```import math
list_of_lists = [[1, 2], [1, 2, 3, 4], [1]]
squared = [math.sqrt(item) for inner_list in list_of_lists for item in inner_list]
print(squared)
```

The output of this code snippet:

```[1.0, 1.4142135623730951, 1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 1.0]
```

This snippet calculates the square root of every number in each inner list and stores them in a new flattened list called `squared`. Using list comprehension, the code is shortened and the intent is clear—apply a function to all items in a nested structure.

## Method 3: Using itertools.chain

The `itertools.chain()` function from Python’s itertools module is designed for efficient looping. It can be used to iterate over all the elements in multiple iterables—like our lists of different lengths—without the need for a nested loop structure.

Here’s an example:

```from itertools import chain
list_of_lists = [[1, 2], [1, 2, 3, 4], [1]]
flattened = list(chain.from_iterable(list_of_lists))
print(flattened)
```

The output of this code snippet:

```[1, 2, 1, 2, 3, 4, 1]
```

By utilizing `itertools.chain.from_iterable()`, we are able to flatten our list of lists into a single list with all the elements. This makes the code not only more readable but also more efficient, especially for larger datasets.

## Method 4: Loop with enumerate

When you need access to the index of the inner lists or their elements, Python’s `enumerate` function within a loop provides a convenient and Pythonic solution. This allows you to maintain the current position in the list, which can be useful for more complex operations or when maintaining state between iterations.

Here’s an example:

```list_of_lists = [[1, 2], [1, 2, 3, 4], [1]]
for idx, inner_list in enumerate(list_of_lists):
print(f'List {idx} elements:')
for item in inner_list:
print(item)
```

The output of this code snippet:

```List 0 elements:
1
2
List 1 elements:
1
2
3
4
List 2 elements:
1
```

This code excerpt uses `enumerate` to print each item of the inner lists along with the index of the respective list. Besides direct access to each item, the `enumerate` function lets you keep track of the list’s position, enhancing code readability and utility.

## Bonus One-Liner Method 5: Using a Generator Expression

A generator expression is similar to a list comprehension, but it produces items one at a time and is memory efficient. If you want to process elements of the list without creating an entire new list, this can be a good approach.

Here’s an example:

```list_of_lists = [[1, 2], [1, 2, 3, 4], [1]]
flattened = (item for inner_list in list_of_lists for item in inner_list)
for num in flattened:
print(num)
```

The output of this code snippet:

```1
2
1
2
3
4
1
```

This snippet uses a generator expression to flatten the list and then iterates over it, printing each number. This offers the benefit of being both memory-efficient and convenient for processing large lists where you may not need all the elements at once.

## Summary/Discussion

• Method 1: Iterating with a for Loop. Offers simplicity and direct control over each element. Not the most efficient for larger datasets.
• Method 2: List Comprehension. Provides a more concise and often more readable code structure compared to a standard loop, especially for simple transformations or flattening.
• Method 3: Using itertools.chain. Very efficient for iterating over multiple lists or combining them into a single iterable object without actually creating a new list in memory.
• Method 4: Loop with enumerate. Gives you access to both the index and the items, making it suitable for more complex operations where state needs to be maintained.
• Method 5: Using a Generator Expression. Memory efficiency is the highlight here, as it produces elements one by one without storing them all simultaneously.