Home > OS >  Is there a more elegant way or a shorter one of turning a list element to int, incrementing it and t
Is there a more elegant way or a shorter one of turning a list element to int, incrementing it and t

Time:02-26

The code works just fine, its just ugly to llok at, especially as in my case i have to do that not only previous and next, but upper 3, lower 3 in a 2d list, so it becomes quite verbose. Is there a shorter way of doing this, maybe using "it"?

val nums = mutableListOf("0", "X", "0", "X", "0")

    for (num in 0 until nums.size) {
        if (nums[num] == "X") {
            var a = nums[num - 1]
            if ( nums[num - 1] != "X") nums[num - 1] = (nums[num - 1].toInt()   1).toString()
            if ( nums[num   1] != "X") nums[num   1] = (nums[num   1].toInt()   1).toString()
        }
    }

CodePudding user response:

Instead of looking for "X" and increasing values around it, you could look for numbers and alter them based on their surrounding values. This way, you can make use of the Iterable<T>.map or Iterable<T>.mapIndexed functions.

For every element, you just have to look at the surrounding amount of values, both before and after the element. One way of doing so, is using List<out E>.subList(fromIndex: Int, toIndex: Int). As far as I understood, the value should be increased for every "X" found in that sublist.

Combining this, you may end up with something along the lines of:

fun increase(numbers: List<String>, surrounding: Int): List<String> {
    return numbers.mapIndexed { index, value ->
        val intValue = value.toIntOrNull()
            ?: return@mapIndexed value

        val lowerSublistBound = (index - surrounding).coerceAtLeast(0)
        val upperSublistBound = (index   surrounding   1).coerceAtMost(numbers.size)

        val increaseBy = numbers.subList(lowerSublistBound, upperSublistBound).count { it == "X" }

        "${intValue   increaseBy}"
    }
}

It's not shorter per-se but works with an arbitrary amount of surrounding values, instead of hard coding to retrieve the n preceding and following elements.

Using this, yields the following results:

fun main() {
    val baseNumbers = listOf("X", "X", "0", "1", "X", "2", "X")

    println(increase(baseNumbers, 0) == baseNumbers)
    println(increase(baseNumbers, 1) == listOf("X", "X", "1", "2", "X", "4", "X"))
    println(increase(baseNumbers, 2) == listOf("X", "X", "3", "3", "X", "4", "X"))
    println(increase(baseNumbers, 3) == listOf("X", "X", "3", "5", "X", "4", "X"))
    println(increase(baseNumbers, 4) == listOf("X", "X", "4", "5", "X", "5", "X"))
    println(increase(baseNumbers, 5) == listOf("X", "X", "4", "5", "X", "6", "X"))}

CodePudding user response:

So far your code is the shortest way to do it, yet the hardest to understand and maintain, probably 2 months after writing. As an example; your code currently works for the sequence ("0", "X", "0", "X", "0") but will not for ("X", "0", "X", "0", "X); it's still the same sequence, but only with a different permutation in the latter. Try making it work for the latter, you would need a considerable amount of time, and most probably change every line in the loop.

From methods that supply it, the most common list.forEach, do not allow you to modify the list from within the it block, as the iterator would run into a ConcurrentModificationException; but in this case, the list needs to be modified in each iteration, so that would not work unless you create a temporary list which can be modified from within the block, but hell NO, memory is limited.

As extracted from the Programming principles, if you find yourself needing the same piece of code every time and only modifying a tiny bit of it every time, then what you need is a function, and probably function overloading. In this case, creating a function that takes the list, nums, transforms it, and returns the transformed list would solve your verbosity problem; but why not, that's how println function works.

Long Elegant Solution, shortened into a one-line by a function: From your input, these assumptions are made:

  1. "X" in the list works like a 'magic number', as used in file MIME types; i.e. once we find an "X" increment the valid values to its left and right.
  2. There may exist a stringed number, to the left or to the right of the 'magic number' above, iff it's not also an "X", then Int.toString() would not throw a NumberFormatException.
  3. ("0", "X", "0", "X", "0") and ("X", "0", "X", "0", "X) are considered valid inputs, coz why not?

The comments explain:

val nums = mutableListOf( "0", "X", "0", "X", "0")

// Let's see what is contained in [nums] before we transform it
println("\tNums Before: $nums")

    /*
    This keeps the valid range of indices available in [nums],
    anything out of this range would produce an IndexOutOfBounds exception
    */
    val numsRange = IntRange(0, nums.size - 1)
    for (num in nums.withIndex()) {
        val currentIndex = num.index // The current index in [nums] the loop is currently processing
        val nextIndex = currentIndex   1 // The next index in [nums] relative to [currentIndex]
        val previousIndex = currentIndex - 1 // The previous index in [nums] relative to [currentIndex]

        // The current value in [num], maybe "X" or "0"
        val currentNum = num.value
        /* Try getting the number to the right of [currentNum] in the list, [num], or null if it does not exist */
        val nextNum = if (numsRange.contains(nextIndex)) nums[nextIndex] else null
        /* Try getting the number to the left of [currentNum] in the list, [num], or null if it does not exist */
        val previousNum = if (numsRange.contains(previousIndex)) nums[previousIndex] else null

        if (currentNum == "X") {
            // Found the magic number
            if (nextNum != "X" && nextNum != null)
                // Only increment the number to the right of current "X" if it exists
                nums[nextIndex] = (nextNum.toInt()   1).toString()
            if (previousNum != "X" && previousNum != null)
                // Only increment the number to the left of current "X" if it exists
                nums[previousIndex] = (previousNum.toInt()   1).toString()
        }

    }
// Let's see what is contained in [nums] after the transformation
println("\tNums After:  $nums") 

Now as a function; comments removed for brevity:

fun increment(nums: MutableList<String>) {
    val numsRange = IntRange(0, nums.size - 1)
    for (num in nums.withIndex()) {
        val currentIndex = num.index
        val nextIndex = currentIndex   1
        val previousIndex = currentIndex - 1

        val currentNum = num.value
        val nextNum = if (numsRange.contains(nextIndex)) nums[nextIndex] else null
        val previousNum = if (numsRange.contains(previousIndex)) nums[previousIndex] else null

        if (currentNum == "X") {
            if (nextNum != "X" && nextNum != null)
                nums[nextIndex] = (nextNum.toInt()   1).toString()
            if (previousNum != "X" && previousNum != null)
                nums[previousIndex] = (previousNum.toInt()   1).toString()
        }
    }
}

Let's call the function 10 times and see the effects it has on the list on each invocation:

 val numsFunc = mutableListOf("0", "X", "0", "X", "0")

    for (i in 0..9){
        println("loop: $i")
        println("\tNums Func Before: $numsFunc")
        increment(numsFunc)
        println("\tNums Func After:  $numsFunc")
    }

Output:

loop: 0
    Nums Func Before: [0, X, 0, X, 0]
    Nums Func After:  [1, X, 2, X, 1]
loop: 1
    Nums Func Before: [1, X, 2, X, 1]
    Nums Func After:  [2, X, 4, X, 2]
loop: 2
    Nums Func Before: [2, X, 4, X, 2]
    Nums Func After:  [3, X, 6, X, 3]
...
...
  • Related