Обновить объекты в списке из объектов в другом списке в kotlin - PullRequest
2 голосов
/ 17 мая 2019

Есть ли лучший способ написать следующее:

private fun updateImportantProperty(
    firstList: List<MyObjectX>?,
    secondList: List<MyObjectX>?
) {
    firstList?.forEach { item1 ->
        secondList?.forEach { item2 ->
            if (relatesInSomeWayToEachOther(item1, item2)) {
                item1.importantProperty = item2.importantProperty
            }
        }
    }
}

Результатом вышеприведенного кода может быть то, что 1 объект firstList был обновлен или что 7 объектов были (если в списках было 7 объектов вобщие).

Я просто хочу обновить это важное свойство объектов в firstList.Не предполагайте ничего из списка, такого как сортировка или размер, или если все объекты находятся в обоих списках.Читаемость - это то, что мне нужно.

Ответы [ 3 ]

1 голос
/ 18 мая 2019

Таким образом, вы в основном хотите отобразить первый список, заменив определенное значение свойства последним соответствующим свойством из второго списка.Конечно, было бы проще, если бы вы дали немного больше контекста, но я постараюсь упростить ваш синтаксис:

firstList?.map { 
  firstListElement -> secondList
    ?.filter { it.relatesInSomeWayTo(firstListElement) }
    ?.lastOrNull()
    ?.let { firstListElement.copy(property = it.property) }
      ?: firstListElement 
}

Я считаю, что это было бы более по-котлински.

Обратите вниманиетакже две необязательные настройки - во-первых, ваша функция «relatedTo» должна быть расширением MyObjectX.Во-вторых, он должен быть неизменным и, следовательно, использовать copy вместо присваивания свойства.

РЕДАКТИРОВАТЬ: извините, это не скомпилировать.Функция filter возвращает список, а не объект, поэтому вам нужно добавить lastOrNull() call.Исправлено;)

1 голос
/ 17 мая 2019

Если списки становятся большими, итерация по всему второму списку для каждого из элементов первого списка становится медленной, поскольку это занимает O(n^2) время.

Если совпадающие элементы встречаются редко (т. Е. Совпадений для каждого из элементов первого списка не будет много, всего несколько), вы можете создать какой-то индекс, извлекая то, что действительно имеет смысл для relatesInSomeWayToEachOther из всех элементов второго списка, а затем выполнить быстрый поиск для каждого элемента первого списка.

Например:

val secondListIndex = secondList.groupBy { getIndexKey(it) }

firstList.forEach { item1 ->
    val matchingSecondListItems = secondListLookup[getLookupKey(item1)].orEmpty()
    matchingSecondListItems.forEach { item2 ->
        item1.importantProperty = item2.importantProperty
    }
}

Я использовал две функции: getIndexKey и getLookupKey, но это может быть одна и та же функция, если критерии соответствия просты.

0 голосов
/ 17 мая 2019

Предполагая, что Map не имеет смысла, и, как вы упомянули, сложность по времени не является проблемой, я сконцентрируюсь только на том, как можно улучшить этот код с точки зрения читабельности.

Тем не менее, я бы добавил небольшое улучшение производительности, т. Е. Вам не нужно вызывать firstList.forEach, если второй список пуст / null. Кроме того, так как это выглядит довольно особым случаем, я бы предпочел добавить для него функцию расширения, чтобы было еще более понятно, какой список обновляется чем, например ::

.
fun List<MyObjectX>.updateBy(other: List<MyObjectX>?) {
  if (other != null && other.isNotEmpty())
    forEach { thisItem ->
      other.filter { relatesInSomeWayToEachOther(thisItem, it) }
           .forEach { thisItem.importantProperty = it.importantProperty }
    }
}

так что его использование более читабельно, я думаю, например:

val oneNullable : List<MyObjectX>? = ...
val twoNotNull : List<MyObjectX>? = ...

oneNullable?.updateBy(twoNotNull)
// or vice-versa
twoNotNull.updateBy(oneNullable)

Что касается объектов, которые должны обновляться не более одного раза, то внутренний цикл слегка изменяется:

fun List<MyObjectX>.updateOnceBy(other: List<MyObjectX>?) {
  if (other != null && other.isNotEmpty()))
    forEach { thisItem ->
      other.firstOrNull { 
        relatesInSomeWayToEachOther(thisItem, it)
      }?.also {
        thisItem.importantProperty = it.importantProperty
      }
    }
}

Хорошо ... Понятно ... Я ценю читаемость на стороне вызывающей стороны выше, чем на стороне реализации; -)

Может быть, разделение поможет улучшить читаемость:

fun MyObjectX.updateBy(other: List<MyObjectX>) {
  other.filter { this == it } // or if only first applies, firstOrNull again
       .forEach { importantProperty = it.importantProperty }
}

fun List<MyObjectX>.updateBy(other: List<MyObjectX>?) {
  if (other != null && other.isNotEmpty())
    forEach {
      it.updateBy(other)
    }
}

Но я думаю, что что-то вроде Map не только улучшит сложность времени, но и удобочитаемость ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...