Котлин — создание карты с 3 массивами с использованием сгиба не работает

#kotlin

#котлин

Вопрос:

У меня есть множество клиентов, у каждого клиента есть свойства id uuid subCustomer и другие свойства , которые меня не интересуют. Я хотел бы выполнить одну итерацию, в которой я создал бы 3 массива , в которых содержался бы только один ids , другой uuids и третий subcustomers . Я пытался добиться этого с помощью fold такой функции:

 customers.fold(  mapOf(  "ids" to listOflt;Stringgt;(),  "uuids" to listOflt;UUIDgt;(),  "subCustomers" to listOflt;Stringgt;()  ))  { acc, customer -gt;  acc["ids"]?.plus(customer["id"])  acc["uuids"]?.plus(customer["uuid"])  acc["subCustomers"]?.plus(customer["subCustomer"])  }  

С помощью этого кода я получаю ошибку в редакторе:

 Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Listlt;Any?gt;?  

Я тоже пробовал это:

 customers.fold(  mapOf(  "ids" to listOflt;Stringgt;(),  "uuids" to listOflt;UUIDgt;(),  "subCustomers" to listOflt;Stringgt;()  ))  { acc, customer -gt;  mapOf(  "ids" to acc["ids"]?.plus(customer["id"]),  "uuids" to acc["uuids"]?.plus(customer["uuid"]),  "subCustomers" to acc["subCustomers"]?.plus(customer["subCustomer"])  )  }  

Но я получаю эти ошибки:

 Type mismatch. Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt; Found: Listlt;Any?gt;? Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Maplt;String, Listlt;Any?gt;?gt;  

Комментарии:

1. Пожалуйста , не используйте карты для этих целей. Напишите классы данных.

2. Как добавить в список в классе данных?

Ответ №1:

Напишите два класса данных для ваших данных. Один для ваших клиентов и один для трех списков, которые вы хотите:

 data class Customer(  val id: String,  val uuid: UUID,  val subCustomer: String,  ) data class CustomerDataLists(  val ids: MutableListlt;Stringgt; = mutableListOf(),  val uuids: MutableListlt;UUIDgt; = mutableListOf(),  val subCustomers: MutableListlt;Stringgt; = mutableListOf(),  )  

Затем просто используйте простой цикл for для добавления данных в:

 val dataLists = CustomerDataLists() for (customer in customers) {  dataLists.ids.add(customer.id)  dataLists.uuids.add(customer.uuid)  dataLists.subCustomers.add(customer.subCustomer) } // now dataLists is filled with customers' data  

Комментарии:

1. Я бы не советовал использовать var с изменяемыми списками. Я думаю, что это должно быть либо var с List типом, либо val с MutableList типом

2. @Джоффри о боже, я забыл, что оставил var там. Я планировал использовать неизменяемые списки, как это сделал ОП, но потом подумал, что это создаст слишком много списков.

Ответ №2:

Ответ @Sweeper хороший. Я считаю, что в любом случае стоит использовать классы данных вместо карт для такого рода вариантов использования.

Поскольку у вас на самом деле нет никаких взаимодействий между 3 списками в fold , вы также можете создавать эти списки независимо (но здесь, конечно, 3 итерации).:

 data class Customer(  val id: String,  val uuid: UUID,  val subCustomer: String, )  data class AggregatedCustomers(  val ids: Listlt;Stringgt;,  val uuids: Listlt;UUIDgt;,  val subCustomers: Listlt;Stringgt;, )  val customers: Listlt;Customergt; = TODO("get that list from somewhere")  val aggregated = AggregatedCustomers(  ids = customers.map { it.id }  uuids = customers.map { it.uuid }  subCustomers = customers.map { it.subCustomer } )  

Ответ №3:

Этот ответ состоит из 3 частей:

  1. Лучший способ решить такую проблему;
  2. Почему исходный код не работает;
  3. На другие проблемы нужно обратить внимание.

1. Лучший способ решить такую проблему

Давайте предположим, что Consumer упомянутое выглядит следующим образом:

 data class Customer(  val id: String,  val uuid: UUID,  val subCustomer: String, )  

В таком случае действительно нет необходимости использовать функцию fold . For функции цикла или расширения forEach просто достаточно:

 val customers: Listlt;Customergt; = listOf(  Customer("1", UUID.randomUUID(), "sub-1"),  Customer("2", UUID.randomUUID(), "sub-2"),  Customer("3", UUID.randomUUID(), "sub-3"), )  val ids = mutableListOflt;Stringgt;() // pay attention. use `mutableListOf` instead of `listOf()` val uuids = mutableListOflt;UUIDgt;() val subConsumers = mutableListOflt;Stringgt;()  customers.forEach {  ids  = it.id  uuids  = it.uuid  subConsumers  = it.subCustomer }  

2. Why the original code doesn’t work

The proposed two pieces of code are in the same pattern:

 customers.fold(  mapOf(  "ids" to listOflt;Stringgt;(),  "uuids" to listOflt;UUIDgt;(),  "subCustomers" to listOflt;Stringgt;()  ) ) { acc, customer -gt;  // ... do something with acc and customer }  

Сначала мы должны прояснить, что последнее утверждение в fold области является выражением, которое необходимо накопить. Это как acc_n lt;combinegt; customer -gt; acc_(n 1) для каждого клиента в customers каждый момент времени , где lt;combinegt; мы пишем нашу логику. Таким образом, первый предложенный фрагмент кода не работает, потому что вы можете не знать, что что-то должно быть возвращено во время написания:

 customers.fold(...){ acc, customer -gt;  acc["ids"]?.plus(customer.id)  acc["uuids"]?.plus(customer.uuid)  acc["subCustomers"]?.plus(customer.subCustomer) }  

Фактически, последнее утверждение acc["subCustomers"]?.plus(...) является выражением с типом Listlt;Anygt;? , котлин рассматривает его как ваше «acc_(n 1)», но вы предлагаете mapOf("ids" to ...) как acc_0 , у которого есть тип Maplt;String, ...gt; : не тот же тип, Listlt;Anygt;? что и . И вот почему вы получили первую ошибку:

 Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;?  

Мы поговорим об универсальных типах позже.

Давайте перейдем ко второму фрагменту кода. В качестве последнего выражения в области действия предлагается fold карта , которая также является картой:

 customers.fold(...) { acc, customer -gt;  mapOf(  "ids" to acc["ids"]?.plus(customer.id),  "uuids" to acc["uuids"]?.plus(customer.uuid),  "subCustomers" to acc["subCustomers"]?.plus(customer.subCustomer)  ) }  

Самый простой способ устранить ошибку-использовать !! выражение (не рекомендуется!):

 customers.fold(...) { acc, customer -gt;  mapOf(  "ids" to acc["ids"]?.plus(customer.id)!!,  "uuids" to acc["uuids"]?.plus(customer.uuid)!!,  "subCustomers" to acc["subCustomers"]?.plus(customer.subCustomer)!!  ) }  

Причина в том, что kotlin не может утверждать, что acc[«идентификаторы»] не является нулевым, поэтому вы используете ?. для вызова метода, безопасного для нуля. Однако такой вызов делает возвращаемый тип обнуляемым:

 val cus: Customer? = Customer("1", UUID.randomUUID(), "sub-1") // cus has type Customer? : nullable val id1: String = cus?.id // [compile error] Type mismatch. [Required: String] [Found: String?] val id2: String? = cus?.id // OK val id3: String = cus?.id!! // If `cus?.id` is null, throw NPE.  

Вы объявили acc_0 (в скобках после fold ) в типе Maplt;String, Listlt;Tgt;gt; неявно (мы поговорим о T позже). Просто знайте, что T не является типом, допускающим обнуление), но карта с типом Maplt;String, Listlt;Tgt;?gt; была найдена как acc_(n 1) . Несоответствие типов и ошибка были показаны:

 Type mismatch. Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt; Found: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;?  

3. Другая проблема, на которую нужно обратить внимание

Важная проблема заключается в следующем: что это за тип acc_0 ?

 // acc_0:  mapOf(  "ids" to listOflt;Stringgt;(),  "uuids" to listOflt;UUIDgt;(),  "subCustomers" to listOflt;Stringgt;() )  

Конечно, тип каждого выражения слева to -это строка, и Listlt;Tgt; это тип каждого выражения справа от нее. так и должно быть Maplt;String, Listlt;Tgt;gt; . О чем T ? Котлин пытается найти ближайшего предка String и UUID и найти их обоих, реализующих Comparablelt;?gt; и Serializable , так что это то, что вы видите в ошибке. Это тип Т:

 Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;  

This may lead to some unwanted experience:

 val map = mapOf(  "listA" to mutableListOf("233"),  "listB" to mutableListOf(UUID.randomUUID()) ) val listA = map["A"]!! // MutableListlt;out {Comparable{String amp; UUID}gt; amp; java.io.Serializable}!gt; // generic type "collapse" into `Nothing` for no type can implement both Comparablelt;Stringgt; and Comparablelt;UUIDgt; listA.add(Any()) // Type mismatch. [Required: Nothing] [Found: Any]  

So try not to put lists with different generic type into one map.

Another problem is, when you try to invoke acc["ids"]?.plus(customer.id) , you are actually invoking such method (from kotlin _Collections.kt )

 public operator fun lt;Tgt; Collectionlt;Tgt;.plus(element: T): Listlt;Tgt; {  val result = ArrayListlt;Tgt;(size   1)  result.addAll(this)  result.add(element)  return result }  

Новый список создается каждый раз, когда вы вызываете метод! Попробуйте использовать mutableListOf() в замене listOf() для коллекций, в которые вы хотите внести изменения, и вместо этого используйте оператор » =» (или ?.plusAsign() в качестве нулевой безопасной версии). Это может привести к какой-то другой проблеме с исходным кодом (которая слишком сложна, чтобы объяснить, почему), но для кода в части 1: Лучший способ решить такую проблему = фактически вызвать:

 public inline operator fun lt;Tgt; MutableCollectionlt;in Tgt;.plusAssign(element: T) {  this.add(element) }  

которые просто добавляют ценность в список, не создавая новых.