LINQと同じ処理をPythonのリストで行う

最近、本腰を入れてPythonを基礎から勉強し直しています。 そうした中で、「LINQならああやって書くアレは、Pythonではどうやって書くのだろう?」と調べることが増えてきましたので、一旦整理しておきたいと思います。

今回はリストです。 辞書や集合などもまとめるかもです。

リストの生成(Range)

Enumerable.Rangeメソッドに対応する関数として、Pythonではrangeがあります。

# var numbers = Enumerable.Range(1, 5);
numbers = [number for number in range(1, 6)] # [1, 2, 3, 4, 5]

SelectとWhere

forの前で値を取得・変形することでSelectメソッド、if句で絞り込みでWhereメソッドと、それぞれ同じことができます。

組み合わせることで、Whereで絞った要素をSelectで取得するというのも簡単にできます。

# Select
# var squareNumbers = numbers.Select(i => i*i);
square_numbers = [i**2 for i in numbers] # [1, 4, 9, 16, 25]

# Where
# var oddNumbers = numbers.Where(i => i % 2 == 1);
odd_numbers = [i for i in numbers if i % 2 == 1] # [1, 3, 5]

# 組み合わせる
# var oddSquareNumbers = numbers.Where(i => i % 2 == 1).Select(i => i*i);
odd_square_numbers = [i**2 for i in numbers if i % 2 == 1] # [1, 9, 25]

TakeとSkip

内包表記の場合、enumerate関数でリストのインデックスと値の両方を同時に取得できます。 インデックスをif句で絞り込むことで、TakeやSkipメソッドと同じことができます。

ただ、単に「先頭から何番目まで」「末尾から何番目まで」という絞り込みだけであれば、添字指定の方が簡略で読みやすいです。

# Take
# var takeNumbers = numbers.Take(2);
take_numbers = [number for index, number in enumerate(numbers) if index < 2] # [1, 2]
# または
# take_numbers = numbers[0:2]

# Skip
# var skipNumbers = numbers.Skip(2);
skip_numbers = [number for index, number in enumerate(numbers) if index >= 2]
# または
# skip_numbers = numbers[2:]

# 組み合わせる
# var medianNumbers = numbers.Skip(2).Take(1);
median_numbers = [number for index, number in enumerate([number for index, number in enumerate(numbers) if index >= 2])  if index < 1] # 3
# または
# median_numbers = numbers[2:3]

FirstとLast

ポイントはifで絞り込んだ後に添字を指定することです。

# First
# var firstEvenNumber = numbers.First(i => i % 2 == 0);
first_even_number = [i for i in numbers if i % 2 == 0][0] # 2

# Last
# var lastEvenNumber = numbers.Last(i => i % 2 == 0);
last_even_number = [i for i in numbers if i % 2 == 0][-1] # 4

判定(Any/All/Contains)

判定用の関数としてanyとallが提供されています。 inを使うことでリスト中に同じ値があるかどうかを判定できます。

# Any
# var anyEven = numbers.Any(i => i % 2 == 0);
any_even = any([i % 2 == 0 for i in numbers]) # True

# All
# var allEven = number.All(i => i % 2 == 0);
all_even = all([i % 2 == 0 for i in numbers]) # False

# Contains
# var containsThree = numbers.Contains(3);
contains_three = 3 in numbers # True

順序(OrderBy/OrderByDescending)

リストの場合、sorted関数でデフォルトでは昇順ソートされ、引数reverseにTrueを渡すと降順でソートされます。 また、sortedの引数keyにラムダ式でソート方法を任意に設定することもできます。

# OrderBy
# var orderedNumbers = numbers.OrderBy(i => i);
ordered_numbers = sorted(numbers) # [1, 2, 3, 4, 5]

# OrderByDescending
# var disorderedNumbers = numbers.OrderByDescending(i => i);
disordered_numbers = sorted(numbers, reverse=True) # [5, 4, 3, 2, 1]

集約(Sum/Average/Min/Max)

sum、min、maxの集約関数が用意されているのでそれらを使うことで同じことができます。 Averageメソッドに対応するものはありませんが、sumの値をlenで除算することで簡単に導出できます。

# Sum
# var sumNumber = numbers.Sum(i => i);
sum_number = sum(numbers) # 15

# Average
# var averageNumber = numbers.Average(i => i);
average_number = sum(numbers) / len(numbers) # 3.0

# Min
# var minNumber = numbers.Min(i => i);
min_number = min(numbers) # 1

# Max
# var maxNumber = numbers.Max(i => i);
max_number = max(numbers) # 5

Zip

zip関数が提供されており、これを使うことで2つのリストの値を同時に取得できます。

# Zip
# var addedNumbers = numbers.Zip(squareNumbers, (first, second) => first + second);
added_numbers = [first + second for first, second in zip(numbers, square_numbers)] # [2, 6, 12, 20, 30]