Collect iterables into one variable (list) - kotlin

Consider this:
fun readFiles(directory: String): List<File> {
val result = ArrayList<File>()
File(directory).walkTopDown().forEach {
result.addAll(getFiles(it))
}
return result
}
fun getFiles(file: File): List<File> { ... }
How can rewrite this so I don't need to initialize the result ArrayList but can directly return File(directory).walkTopDown().????
The question is not about what's the best way to read files or anything, just how I can write the above code more concise while doing the same.

You can use flatMap for this purpose. It first maps each element to a Sequence using your mapping function (so you kinda get a Sequence<Sequence<File>>), then it flattens every result back to a Sequence<File>.
Since walkTopDown returns a FileTreeWalk (which is a subclass of Sequence<File>), and you return a List<File>, you have to do some conversions as well. You can remove these conversions if you make getFiles and readFiles return a Sequence<File> instead.
fun readFiles(directory: String): List<File> {
return File(directory)
.walkTopDown()
.flatMap { getFiles(it).asSequence() }
.toList()
}

Related

Is there a scope function returning previous value?

I need to perform some operations on a mutable list and use its previous value. Something similar to also except that it should return the previous value.
val localList: List<String> = mainList.alsoButGetPrevious { it.clear() }
Before I write this function, is there an existing one that does this?
There is no "previous value" in this case. There is only one list and it is modified by clear(), so even if you would do something like this:
localList = mainList
mainList.clear()
Then localList would still not contain "previous" value, but it would be empty.
What you need here is to copy the list before clearing it. There is no scoping function for this, because it is specific to lists only. You can implement it by yourself:
val localList = mainList.copyAndApply { clear() }
inline fun <T> MutableList<T>.copyAndApply(block: MutableList<T>.() -> Unit): List<T> {
val result = toList()
block()
return result
}
As noted by #mightyWOZ, toList() creates a shallow copy of the data, meaning that it contains references to the same object instances as the original list. Above solution works if you need to only clear mainList or add/remove items from it. However, if you modify objects in mainList then this change will affect localList as well. There is no straightforward and universal way to perform a deep copy of the data in Java/Kotlin. If you need this, it is probably better to create utility functions that target your data structures.
As broot's answer says, there is only one list, and you should copy it with toList. You can do this inline with scope functions like this:
val localList: List<String> = mainList.run {
// here, this: MutableList<T>
toList().also { clear() } // here we are calling this.clear()
}
or if you don't mind repeating mainList,
val localList: List<String> = mainList.toList().also {
mainList.clear()
}
You can use Delagetes.observable or vetoable to observe or even with vetoable set under which conditions it should change
var myList: List<String> by Delegates.observable(listOf()) {
property: KProperty<*>, oldValue: List<String>, newValue: List<String> ->
}
var myList: List<String> by Delegates.vetoable(listOf()) { property: KProperty<*>, oldValue: List<String>, newValue: List<String> ->
newValue.size > 5
}

Kotlin add custom method to stream chaining

I need to add a custom method (which is a Consumer) to the dot chaining in stream api, i not sure how to do it, following is my code.
If that is not possible, is there anyway to do it with other operation? Maybe like with .map or something else?
fun main(args: Array<String>) {
var countries: List<String> = listOf("India", "Germany", "Japan")
var firstCountry = countries.stream()
.filter{it == "Germany"}
.performOperation{} //not sure what to do here
.findFirst()
println(firstCountry)
}
fun performOperation(country: String) {
if(country.length > 3) {
throw InvalidLengthException("Error")
}
//do some operation, won't return any value
doCustomOperation(country)
}
You may already be aware that when it comes to steams there are two types of operations, one is your map, filter etc. known as intermediate opeartion and others are terminal operations such as forEach. You said your custom operation wont return any value, hence making it a terminal operation. moreover it seems to me that you want to perform same operation for all the elements, basically a forEach. for this you can define an extension function on Stream as
fun <T> Stream<T>.someOperation(operation: (T) -> Unit){
this.forEach { operation(it) }
}
There is two ways to do what you want.
var firstCountry = countries.stream()
.filter{it == "Germany"}
.also(::performOperation)
.findFirst()
The :: is a function reference and is basically the same as .also { performOperation(it)}
The second one would be to make your own extension method on list. I wouldn't recommend it until you understand kotlin lambdas and extension methods
fun Stream<String>.performOperation(): Stream<String> {
for(country in this) {
if(country.length > 3) {
throw InvalidLengthException("Error")
}
doCustomOperation(country)
}
return this
}
You would just call that one like .performOperation() where you have the .performOperation{}

How can I rewrite this Kotlin filter method more succinctly?

I have a list of strings. I want to search given a query string and say I only want the first 10 matching strings. Here is what I tried, the first one does exactly what I want.
I was wondering if there is a succinct way of writing it.
fun search(query: String): ArrayList<String> {
val found = ArrayList<String>()
for (i in terms.indices) {
if (terms[i].contains(query)) {
found.add(terms[i])
}
if (found.size == 10) {
break
}
}
return found
}
I wanted to use this one but as you can see this is not as efficient as the above one because it goes through the whole list
fun search2(query: String): List<String> {
return terms.filter { it.contains(query) }.take(10)
}
Using a sequence should give you what you want:
fun search2(query: String): List<String> {
return terms.asSequence().filter { it.contains(query) }.take(10).toList()
}
You might not need the toList if you're fine with keeping a sequence as result.

Сheck if map contains substring. Kotlin

I have a
val map = Map<String,String>
map.put("Nurseiyt","android")
I want to get a value by subString like:
map["Nurs"] should return "android"
is it possible?
Use kotlin.Collections, there are methods like filter.
Two things - it's better to use regular expression. So, you can even get better control what will be returned. And the second one, there can be more than one elements matched to that regex. So that's why I return list.
fun <T> substringKey(map: Map<String, T>, regex: Regex): List<T> {
return map.filter { it.key.contains(regex) }
.map { it.value }
}
If you want to use that notation you need to create your own map and override proper operator. What's worth to notice, you cannot return list of values then. So, in this case I just return first found value.
class SubstringMap<V> : HashMap<String, V>() {
override operator fun get(key: String): V? {
return this.entries.first { it.key.contains(key) }.value
}
}
fun main() {
val map = SubstringMap<String>()
map["Nurseiyt"] = "android"
println(map["Nurs"]) // "android"
}
And as the last thing - in kotlin you can create your own operator, like withKeyPart. This would be much better than overriding default operator (because I wouldn't expect that [] operator will work in different way than usual.
infix fun <V> Map<String, V>.withKeyPart(keyPart: String): List<V> {
return this.filter { it.key.contains(keyPart) }
.map { it.value }
}
and then call it like this:
fun main() {
val map = HashMap<String, String>()
map withKeyPart "KeyPart" // infix notation
map.withKeyPart("KeyPart") // standard call
}
Filtering the map, as per other answers, is simple and straightforward, but it doesn't scale well; it takes time proportional to the size of the map, so if the map could grow big, it could get very slow.
If you're always going to be searching for a leading substring, i.e. the start of a map key, then a better general solution is a data structure called a trie.  This lets you search efficiently, with just one lookup per character.
Of course, writing one from scratch may not be justified for your project.  But there are third-party implementations you could use, such as this one in Apache Commons.  Or see the answers to this question.
write top level function like this
fun HashMap<String, String>.getContainskeyValue(search: String): String?
{
var returnList = ArrayList<String?>()
this.keys.filter { it.contains(search) }.map {
returnList.add(this[it])
}
return returnList.first()
//if you want all keys 'contains' values just return list
/* Ex
map.put("Nurseiyt", "android")
map.put("Nurseiyt1", "androidone")
map.put("Nurseirt2", "andrrroidtwo")
val isContainsdata = map.getContainskeyValue("N")
println(" result " + containsdata)
output :result [andrrroidtwo, android, androidone]
*/
}
then call like this
val map = HashMap<String, String>()
map.put("Nurseiyt", "android")
val containsdata = map.getContainskeyValue("Nurs")
println(" result " + containsdata)
output
android

Is there a way to construct a HashSet with initializator function in Kotlin?

To read Stars from a file in the Facebook Hacker Cup's 2016 Boomerang Constelations problem, following extension function can be defined:
fun BufferedReader.readStars(n: Int): Set<Star> {
return Array(n) {
val (l1, l2) = readLine().split(" ").map { it.toInt() }
Star(l1, l2)
}.toHashSet()
}
Code is compact but the values are first read into an array and then converted to a HashSet. Is there a way to directly initialize a HashSet with the size of n and initializator function in Kotlin?
UPDATE: Is there an existing way in standard Kotlin libs?
You can always use apply to initialize objects in-place:
HashSet<Star>(n).apply {
repeat(n) {
val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
put(Star(l1, l2))
}
}
If that's too inconvenient too type every time, write an extension function:
inline fun <T> createHashSet(n : Int, crossinline fn: (Int) -> T) = HashSet<T>(n).apply {
repeat(n) { add(fn(it)) }
}
Usage:
createHashSet<Star>(n) {
val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
Star(l1, l2)
}
Since HashSet is a java class so you can only initialize it in a way provided by JDK.
While there's no helper method in Kotlin runtime it's easy to write it yourself like so:
public fun <T> hashSetOf(size: Int, initializer: (Int) -> T): HashSet<T> {
val result = HashSet<T>(size)
0.rangeTo(size - 1).forEach {
result.add(initializer(it))
}
return result
}
As #miensol has pointed out HashSet initialization is limited to the constructors made available by the JDK. Kotlin has added a hashSetOf function which initializes an empty HashSet and then adds the specified elements to it.
To avoid first reading the values into an array you can use a kotlin.Sequence who's "values are evaluated lazily":
fun BufferedReader.readStars(n: Int): Set<Star> {
return lineSequence().take(n).map {
val (l1, l2) = it.split(" ").map { it.toInt() }
Star(l1, l2)
}.toHashSet()
}
It seems like you are asking an XY question (http://xyproblem.info/). You really want to know how to write readStars in the most efficient way, but instead you ask about HashSet. I think #mfulton26 answers your question as well depending on what is being asked.
Here is the answer for "how do I write this in the most efficient way:"
You have two options. First, a version that auto-closes the stream at the end:
fun BufferedReader.readStars(n: Int): Set<Star> {
return use {
lineSequence().map { line ->
val idx = line.indexOf(' ')
Star(line.substring(0, idx).toInt(), line.substring(idx + 1).toInt())
}.toSet()
}
}
And second, a version that does not:
fun BufferedReader.readStars(n: Int): Set<Star> {
return lineSequence().map { line ->
val idx = line.indexOf(' ')
Star(line.substring(0, idx).toInt(), line.substring(idx+1).toInt())
}.toSet()
}
Neither version creates an array, neither do they make copies of data. They stream the data through a sequence which creates the Set and fills it directly.
Other notes
No need to use split if you are really concerned about allocations and performance. Just use indexOf(char) and split the string yourself using substring.
If you do a split, then please use split(char) not split(String) when you are looking to split on a char