Kotlin: перебирать список и добавлять элементы в индекс

У меня есть (изменяемый) список с элементами. Я хочу пройтись по этому списку и проверить каждый элемент на предмет определенного условия. Когда условие выполнено, я хочу вставить (не заменить) новый элемент по определенному индексу. Какой самый элегантный способ сделать это в Котлине?
P.S. У меня еще нет образца кода, потому что я не могу придумать, как это сделать.

Изменить: вариант использования: я хочу добавить заголовки для представления списка для определенной группы элементов.

Вы также проверите состояние нового добавленного предмета?

EpicPandaForce 10.08.2018 15:33

Какой вариант использования? Несмотря на то, что я добавил возможное решение, я не могу придумать вариант использования, который того стоит.

Roland 10.08.2018 15:57

В зависимости от того, что вы подразумеваете под «конкретным индексом», flatMap может делать именно то, что вы хотите. Однако он создает новую карту, а не обновляет старую.

Paul Georg Podlech 10.08.2018 16:08
7
3
17 654
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Как вы сказали, вы перебираете список, возможно, вам больше подходит flatMap (в этом примере я добавляю «odd», «even» после элементов, которые являются нечетными / четными):

val list = listOf("1", "2", "3", "4")
val newList = list.flatMap {
      // well... actually your conditions...
      when (it.toInt() % 2) {
        0 -> listOf(it, "<-- that was even" /*, whatever you want to add after it */)
        else -> listOf(it, "<-- that was odd" /*, whatever you want to add after it */)
      }
    }

Это вернет вам новый список, содержащий созданные вами элементы, например.

[1, <-- that was odd, 2, <-- that was even, 3, <-- that was odd, 4, <-- that was even]

Таким образом, вам не обязательно нужен изменяемый список. И вы довольно быстро понимаете, что объединено в список (последовательность / что угодно).

Если вы действительно хотите сохранить изменяемый список и работать с ним, вам необходимо убедиться, что вы не изменяете список во время работы с ним, иначе вы получите ConcurrentModificationException. С другой стороны, это означает, что вам нужно удерживать индексы, куда вы хотите вставить элементы. Как только вы их вставляете, ваши индексы удержания становятся недействительными, однако если какой-либо из ваших индексов удержания выше, чем ранее добавленный. За исключением того, что вы тоже перемещаете курсор (т.е. добавляете в индекс количество ранее добавленных элементов). Тогда проще просто вставить их задом наперед. Он следует за образцом, в котором список должен соответствовать ключам карты и вставлять свои значения:

val list = mutableListOf("1", "2", "3", "4", "5", "6")

val itemsToInsertAfterMatch = mapOf("1" to "odd 1", "2" to "even", "3" to "odd", "4" to "ev4n")
list.mapIndexedNotNull { index, s -> itemsToInsertAfterMatch[s]?.let { index to it } }
    .reversed() // actually this ensures that we are operatoring on a separate list while we will add elements later
    .forEach { list.add(it.first, it.second) }

Тем не менее, я не могу это рекомендовать. Если вы не обязаны (а кто вас заставляет?), Я бы не стал использовать такую ​​конструкцию. Он скорее скрывает, что происходит (например, что с чем совпадает? И что вставлено, когда и где?).

Все вышеперечисленные решения еще не касались случая, когда элемент не был найден (например, indexOf возвращает -1 или itemsToInsertAfterMatch[s] == null). Однако добавить этот случай должно быть достаточно легко.

Обратите внимание: если то, что вы пытаетесь вставить, является статическим, решение @mTaks с индексами, конечно, проще, чем сопоставление, которое я представил выше. В зависимости от того, насколько сложно ваше состояние, самих индексов вам не хватит. И я могу только порекомендовать вам использовать вместо него flatMap. Когда вернешься, читать будет намного легче. Решение indices / mapIndexed скорее скрывает происходящее. Возможно, вам придется расшифровывать это каждый раз, когда вы к этому вернетесь. Вы даже не думаете в индексах, так зачем? Но может это только мое мнение ;-)

Это мой подход:

val list = mutableListOf("ab", "CD", "ef", "gH")

list.indices
        .filter { list[it].toLowerCase() == list[it] }
        .mapIndexed { index, it -> it + index }
        .forEach { list.add(it, "XX") }

list.forEach { println(it)  }

напечатает: XX ab CD XX ef gH

Условие состоит в том, что если все элементы списка написаны в нижнем регистре, перед ним вставляется элемент «XX».

Если вас беспокоит только элегантность или вы хотите изучить возможности Kotlin в академических или исследовательских целях, это нормально, но я не уверен, что всегда модный способ является наиболее эффективным.
Почему бы не использовать традиционный шлейф while:

var index = 0

while (index < list.size) {
    if (list[index].toLowerCase() == list[index]) {
        list.add(index, "XX")
        index++
    }

    index++
}

Это не изящно, но наверняка действенно и, возможно, более эффективно, чем необычный способ.

Если у вас есть изменяемый список, вы можете получить его изменяемый listIterator и изменить список с помощью этого итератора во время итерации.

Например, чтобы вставить строку после для каждого элемента, представляющего четное число:

val list = mutableListOf("1", "2", "3", "4")

val iterator = list.listIterator()
for (item in iterator) {
    if (item.toInt() % 2 == 0) {
        iterator.add("^ That was even")
    }
}

list.forEach { println(it) }    

Или вставить строку перед в текущую:

val iterator = list.listIterator()
for (item in iterator) {
    if (item.toInt() % 2 == 0) {
        iterator.previous()
        iterator.add("Next will be even:")
        iterator.next()
    }
}

list.forEach { println(it) }
Ответ принят как подходящий

Решил с помощью groupBy

val map = mutableList.map {
    val groupItems = it.groupBy { (it as ListItem).section}
    val mappedItems = mutableListOf<ListBaseItem>()
    groupItems.forEach { section, items ->
        mappedItems.add(ListHeaderItem(section, section))
        mappedItems.addAll(items)
    }
    mappedItems
}

Не хочешь объяснить? Потому что этот код не имеет отношения к вопросу в том виде, в котором он был сформулирован.

user8959091 13.08.2018 19:06

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

Thommy 15.08.2018 09:53
как в вопросе сказано правда? Вы разместили вопрос, который не имел ничего общего с заголовками и группами, затем через 3 дня вы отредактировали его и упомянули заголовки в виде списка, не давая подсказки, а позже вы даете «ответ», который вы можете иметь отношение только к вопросу?
user8959091 15.08.2018 10:00

Я редактировал, так как было непонятно, о чем я просил. А потом я получил решение и опубликовал его. Это сайт, где люди помогают друг другу. Не знаю, удаляете ли вы все вопросы, на которые ответили сами, но я этого не делаю, возможно, найденный мной ответ поможет другим.

Thommy 15.08.2018 10:13

Вы отредактировали через 3 дня после того, как получили 3 ответа на исходный вопрос и не предоставили никакой конкретной информации о заголовках и группах. Таким образом, предоставленный вами ответ не имеет ничего общего с исходным вопросом. Вы должны удалить вопрос и повторно опубликовать его со всеми соответствующими пояснениями,

user8959091 15.08.2018 12:50

Другие вопросы по теме