1概要

Kotlinコレクションは強力なデータ構造であり、Javaコレクションを超えた多くの有益な方法があります。

この記事では明示的に説明していない他のすべての方法を利用できるように、十分に詳細に説明した少数のフィルタリング方法について説明します。

これらのメソッドはすべて新しいコレクションを返し、元のコレクションは変更されません。

いくつかのフィルタを実行するために、ラムダ式を使用します。ラムダについてもっと読むために、ここで我々のKotlin Lambdaの記事を見てください。


2

ドロップ


コレクションを切り取る基本的な方法から始めましょう。ドロップすることで、コレクションの一部を取り出して、数にリストされている要素の数が足りない新しい

List

を返すことができます。

@Test
fun whenDroppingFirstTwoItemsOfArray__thenTwoLess() {
    val array = arrayOf(1, 2, 3, 4)
    val result = array.drop(2)
    val expected = listOf(3, 4)

    assertIterableEquals(expected, result)
}

一方、** 最後のn個の要素を削除したい場合は、

dropLast

を呼び出します。

@Test
fun givenArray__whenDroppingLastElement__thenReturnListWithoutLastElement() {
    val array = arrayOf("1", "2", "3", "4")
    val result = array.dropLast(1)
    val expected = listOf("1", "2", "3")

    assertIterableEquals(expected, result)
}

それでは、述語を含む最初のフィルタ条件を見てみましょう。

この関数は、条件を満たさない要素に到達するまで、コードを取り出してリストを逆方向に処理します。

@Test
fun whenDroppingLastUntilPredicateIsFalse__thenReturnSubsetListOfFloats() {
    val array = arrayOf(1f, 1f, 1f, 1f, 1f, 2f, 1f, 1f, 1f)
    val result = array.dropLastWhile { it == 1f }
    val expected = listOf(1f, 1f, 1f, 1f, 1f, 2f)

    assertIterableEquals(expected, result)
}


dropLastWhile

は、配列要素が

1f

と等しくない最初のインスタンスまでメソッドが各項目を循環するにつれて、リストから最後の3つの

__1f

__を削除しました。

要素が述語の条件を満たさなくなるとすぐに、メソッドは要素の削除を停止します


dropWhile

は述語をとる別のフィルタですが、

dropWhile

はindex


0 – > n


から機能し、

dropLastWhile

はindex

n – > 0

から機能します。

  • コレクションに含まれているよりも多くの要素を削除しようとすると、空の__Listが残ります


3

取る



drop

と非常によく似ていますが、

take

は与えられたインデックスまたは述語まで要素を保持します。

@Test
fun `when predicating on 'is String', then produce list of array up until predicate is false`() {
    val originalArray = arrayOf("val1", 2, "val3", 4, "val5", 6)
    val actualList = originalArray.takeWhile { it is String }
    val expectedList = listOf("val1")

    assertIterableEquals(expectedList, actualList)
}


  • drop



    take

    の違いは、

    drop

    はアイテムを削除するのに対して、

    take

    はアイテムを保持するということです。

コレクションで利用可能な数よりも多くのアイテムを取得しようとしています – 元のコレクションと同じサイズの

List

が返されるだけです。

  • ここでの重要な注意点は、

    takeIf

    は[.under]#NOT#コレクションメソッドではないということです。

    takeIf

    は述部を使用して

    null

    値を返すかどうかを決定します –

    Optional#filter

    を考えてください。

述語に一致するすべての項目を返される

List

に入れることは関数名のパターンを満たすと思われるかもしれませんが、そのアクションを実行するには

the

filter__を使います。


4

フィルタ



filter

は提供された述語に基づいて新しい

List

を作成します。

@Test
fun givenAscendingValueMap__whenFilteringOnValue__ThenReturnSubsetOfMap() {
    val originalMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
    val filteredMap = originalMap.filter { it.value < 2 }
    val expectedMap = mapOf("key1" to 1)

    assertTrue { expectedMap == filteredMap }
}

フィルタリングするとき、異なる配列のフィルタの結果を累積することを可能にする関数があります。それは

filterTo

と呼ばれ、与えられた可変配列への可変リストのコピーをとります。

  • これにより、いくつかのコレクションを取得し、それらを単一の累積コレクション** にフィルタリングすることができます。

この例では、配列、シーケンス、リスト

その後、3つすべてに同じ述語を適用して、各コレクションに含まれる素数をフィルタリングします。

@Test
fun whenFilteringToAccumulativeList__thenListContainsAllContents() {
    val array1 = arrayOf(90, 92, 93, 94, 92, 95, 93)
    val array2 = sequenceOf(51, 31, 83, 674__506__111, 256__203__161, 15__485__863)
    val list1 = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    val primes = mutableListOf<Int>()

    val expected = listOf(2, 3, 5, 7, 31, 83, 15__485__863, 256__203__161, 674__506__111)

    val primeCheck = { num: Int -> Primes.isPrime(num) }

    array1.filterTo(primes, primeCheck)
    list1.filterTo(primes, primeCheck)
    array2.filterTo(primes, primeCheck)

    primes.sort()

    assertIterableEquals(expected, primes)
}

述語の有無にかかわらずフィルタも

Maps

でうまく機能します。

val originalMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
val filteredMap = originalMap.filter { it.value < 2 }

非常に有益なフィルタメソッドのペアは

filterNotNull



filterNotNullTo

で、これは** すべての

null

要素を単に除外するだけです。

最後に、コレクションアイテムのインデックスを使用する必要がある場合、**

filterIndexed



filterIndexedTo

は、要素とその位置インデックスの両方で述語lambdaを使用する機能を提供します。


5

スライス


スライスを実行するために範囲を使用することもできます。スライスを実行するには、スライスが抽出したい

Range

を定義するだけです。

@Test
fun whenSlicingAnArrayWithDotRange__ThenListEqualsTheSlice() {
    val original = arrayOf(1, 2, 3, 2, 1)
    val actual = original.slice(3 downTo 1)
    val expected = listOf(2, 3, 2)

    assertIterableEquals(expected, actual)
}

スライスは上にも下にも移動できます。


  • Ranges

    を使うときは、範囲ステップサイズも設定することができます。

ステップなしで

range

を使用し、コレクションの範囲を超えてスライスすると、結果

List.

に多数の

null

オブジェクトを作成します

ただし、

a

Range

とsteps

を使用してコレクションの範囲を超えると


ArrayIndexOutOfBoundsException


が発生する可能性があります。

@Test
fun whenSlicingBeyondRangeOfArrayWithStep__thenOutOfBoundsException() {
    assertThrows(ArrayIndexOutOfBoundsException::class.java) {
        val original = arrayOf(12, 3, 34, 4)
        original.slice(3..8 step 2)
    }
}


6. 異なる

この記事で取り上げるもう1つのフィルタは明確です。

このメソッドを使って私たちのリストからユニークなオブジェクトを集めることができます

:

@Test
fun whenApplyingDistinct__thenReturnListOfNoDuplicateValues() {
    val array = arrayOf(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 9)
    val result = array.distinct()
    val expected = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)

    assertIterableEquals(expected, result)
}

セレクタ機能を使用することもできます。

セレクタは、一意性を評価しようとしている値を返します。

セレクタ内のオブジェクトを操作する方法を探るために、

SmallClass

という小さなデータクラスを実装します。

data class SmallClass(val key: String, val num: Int)


SmallClass

の配列を使う:

val original = arrayOf(
  SmallClass("key1", 1),
  SmallClass("key2", 2),
  SmallClass("key3", 3),
  SmallClass("key4", 3),
  SmallClass("er", 9),
  SmallClass("er", 10),
  SmallClass("er", 11))


distinctBy

内ではさまざまなフィールドを使用できます。

val actual = original.distinctBy { it.key }
val expected = listOf(
  SmallClass("key1", 1),
  SmallClass("key2", 2),
  SmallClass("key3", 3),
  SmallClass("key4", 3),
  SmallClass("er", 9))

この関数は変数のプロパティを直接返す必要はありません。

異なる値を決定するための計算も可能です

例えば、各10の範囲(0 – 9、10 – 19、20 – 29など)の数値には、最も近い10に切り捨てることができます。これが、セレクターが設定した値です。

val actual = array.distinctBy { Math.floor(it.num/10.0) }


7. チャンク

Kotlin 1.2の1つの興味深い機能は

チャンク

です。チャンキングは単一の

Iterable

コレクションを取り、定義されたサイズに一致するチャンクの新しい

List

を作成することです。

これは

Arrays

では動作しません。

__Iterable

__s

のみです。

抽出するチャンクのサイズだけでチャンクできます

@Test
fun givenDNAFragmentString__whenChunking__thenProduceListOfChunks() {
    val dnaFragment = "ATTCGCGGCCGCCAA"

    val fragments = dnaFragment.chunked(3)

    assertIterableEquals(listOf("ATT", "CGC", "GGC", "CGC", "CAA"), fragments)
}

またはサイズとトランス:

@Test
fun givenDNAString__whenChunkingWithTransformer__thenProduceTransformedList() {
    val codonTable = mapOf(
      "ATT" to "Isoleucine",
      "CAA" to "Glutamine",
      "CGC" to "Arginine",
      "GGC" to "Glycine")
    val dnaFragment = "ATTCGCGGCCGCCAA"

    val proteins = dnaFragment.chunked(3) { codon ->
        codonTable[codon.toString()]?: error("Unknown codon")
    }

    assertIterableEquals(listOf(
      "Isoleucine", "Arginine",
      "Glycine", "Arginine", "Glutamine"), proteins)
}

上記のDNAフラグメントのセレクターの例は、チャンクされたhttps://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html[こちら]にあるKotlinのドキュメントから抜粋したものです。


  • chunked

    を渡すとき、私たちのコレクションのサイズの約数ではないサイズ。そのような場合、私たちのチャンクリストの最後の要素は単に小さなリストになります。

すべてのチャンクがフルサイズであると仮定して__ArrayIndexOutOfBoundsExceptionが発生しないように注意してください。


8結論

すべてのKotlinフィルタを使用して、アイテムをフィルタ処理するかどうかを決定するためにラムダを適用できます。

これらの関数すべてが

Maps


で使用できるわけではありませんが、

Maps

で機能するすべてのフィルター機能は

Arrays

で機能します。

Kotlinコレクションのドキュメントには、配列だけでフィルタ関数を使用できるのか、その両方で使用できるのかについての情報があります。ドキュメントはhttps://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html[ここ]にあります。

いつものように、すべての例はhttps://github.com/eugenp/tutorials/tree/master/core-kotlin/src/test/kotlin/com/baeldung/kotlin[over on GitHub]で利用可能です。