0

I have a list of Longs in Kotlin and I want to make them strings for UI purposes with maybe some prefix or altered in some way. For example, adding "$" in the front or the word "dollars" at the end.

I know I can simply iterate over them all like:

val myNewStrings = ArrayList<String>()
longValues.forEach { myNewStrings.add("$it dollars") }

I guess I'm just getting nitpicky, but I feel like there is a way to inline this or change the original long list without creating a new string list?

EDIT/UPDATE: Sorry for the initial confusion of my terms. I meant writing the code in one line and not inlining a function. I knew it was possible, but couldn't remember kotlin's map function feature at the time of writing. Thank you all for the useful information though. I learned a lot, thanks.

2
  • Why wouldn't you want to create a new string list? Have you profiled your code and discovered this is a bottleneck? Commented Nov 2, 2020 at 15:41
  • 1
    'I feel like there is a way to inline this or change the original long list without creating a new string list?' This goes against Functional programming approach and best practices in even non functional languages like Java. Always strive to make things immutable, that is why this is default behaviour in Kotlin. It is hard to reason about variables that are constantly being altered, whereas creating a new list with your manipulation is easier to reason about. Commented Nov 2, 2020 at 19:25

4 Answers 4

2

You are looking for a map, a map takes a lambda, and creates a list based on the result of the lambda

val myNewStrings = longValues.map { "$it dollars" }

map is an extension that has 2 generic types, the first is for knowing what type is iterating and the second what type is going to return. The lambda we pass as argument is actually transform: (T) -> R so you can see it has to be a function that receives a T which is the source type and then returns an R which is the lambda result. Lambdas doesn't need to specify return because the last line is the return by default.

Sign up to request clarification or add additional context in comments.

Comments

2

You can use the map-function on List. It creates a new list where every element has been applied a function.

Like this:

val myNewStrings = longValues.map { "$it dollars" }

Comments

2

In Kotlin inline is a keyword that refers to the compiler substituting a function call with the contents of the function directly. I don't think that's what you're asking about here. Maybe you meant you want to write the code on one line.

You might want to read over the Collections documentation, specifically the Mapping section.

The mapping transformation creates a collection from the results of a function on the elements of another collection. The basic mapping function is map(). It applies the given lambda function to each subsequent element and returns the list of the lambda results. The order of results is the same as the original order of elements.

val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })

For your example, this would look as the others said:

val myNewStrings = longValues.map { "$it dollars" }

I feel like there is a way to inline this or change the original long list without creating a new string list?

No. You have Longs, and you want Strings. The only way is to create new Strings. You could avoid creating a new List by changing the type of the original list from List<Long> to List<Any> and editing it in place, but that would be overkill and make the code overly complex, harder to follow, and more error-prone.

Comments

2

Like people have said, unless there's a performance issue here (like a billion strings where you're only using a handful) just creating the list you want is probably the way to go. You have a few options though!

Sequences are lazily evaluated, when there's a long chain of operations they complete the chain on each item in turn, instead of creating an intermediate full list for every operation in the chain. So that can mean less memory use, and more efficiency if you only need certain items, or you want to stop early. They have overhead though so you need to be sure it's worth it, and for your use-case (turning a list into another list) there are no intermediate lists to avoid, and I'm guessing you're using the whole thing. Probably better to just make the String list, once, and then use it?

Your other option is to make a function that takes a Long and makes a String - whatever function you're passing to map, basically, except use it when you need it. If you have a very large number of Longs and you really don't want every possible String version in memory, just generate them whenever you display them. You could make it an extension function or property if you like, so you can just go

fun Long.display() = "$this dollars"
val Long.dollaridoos: String get() = "$this.dollars"

print(number.display())
print(number.dollaridoos)

or make a wrapper object holding your list and giving access to a stringified version of the values. Whatever's your jam


Also the map approach is more efficient than creating an ArrayList and adding to it, because it can allocate a list with the correct capacity from the get-go - arbitrarily adding to an unsized list will keep growing it when it gets too big, then it has to copy to another (larger) array... until that one fills up, then it happens again...

2 Comments

A private extension would make sense, but making it global seems to break the intended use for extensions
It was just an example of a thing you could do if you wanted and if it makes your code feel more readable. Stick it in whatever scope feels right! That's what extensions are for, readability and adding to classes you don't control (like Long) but no you probably wouldn't want Longs to have a "make price string" property all over your app... unless you would! Whatever works for you, basically

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.