Convert stream or sequence to a stream or sequence of summaries - kotlin

Using Kotlin, how would I transform a Sequence of detail objects into a Sequence of summary objects, where each summary object represents a fixed number of detailed objects? It's kind of like map, except it's not one-to-one and it's kind of like reduce except it's not to a single accumulator?
For example:
var hourlyInfo: Sequence<HourlyData> = ...
var dailyInfo: Sequence<DailySummary> = hourlyInfo.somethingMagical()
What about the same problem with a stream instead of a sequence?

If there's a known fixed number of items, you use chunked and then map. When the sequence is iterated, actual lists of each chunk of items will be allocated.
var dailyInfo: Sequence<DailySummary> = hourlyInfo
.chunked(12)
.map(::DailySummary) // for example if it has a constructor taking a List of 12 HourlyData
If the List of HourlyData isn't ordered, or there isn't some fixed size, then I suppose you have to group by some property in HourlyData and then map it. This does not preserve Sequence status.
var dailyInfo: Sequence<DailySummary> = hourlyInfo
.groupBy(HourlyData::hourOfDay)
.map { (hour, listOfHourlyData) -> DailySummary(hour, listOfHourlyData) }

Related

What is the most efficient way to join one list to another in kotlin?

I start with a list of integers from 1 to 1000 in listOfRandoms.
I would like to left join on random from the createDatabase list.
I am currently using a find{} statement within a loop to do this but feel like this is too heavy. Is there not a better (quicker) way to achieve same result?
Psuedo Code
data class DatabaseRow(
val refKey: Int,
val random: Int
)
fun main() {
val createDatabase = (1..1000).map { i -> DatabaseRow(i, Random()) }
val listOfRandoms = (1..1000).map { j ->
val lookup = createDatabase.find { it.refKey == j }
lookup.random
}
}
As mentioned in comments, the question seems to be mixing up database and programming ideas, which isn't helping.
And it's not entirely clear which parts of the code are needed, and which can be replaced. I'm assuming that you already have the createDatabase list, but that listOfRandoms is open to improvement.
The ‘pseudo’ code compiles fine except that:
You don't give an import for Random(), but none of the likely ones return an Int. I'm going to assume that should be kotlin.random.Random.nextInt().
And because lookup is nullable, you can't simply call lookup.random; a quick fix is lookup!!.random, but it would be safer to handle the null case properly with e.g. lookup?.random ?: -1. (That's irrelevant, though, given the assumption above.)
I think the general solution is to create a Map. This can be done very easily from createDatabase, by calling associate():
val map = createDatabase.associate{ it.refKey to it.random }
That should take time roughly proportional to the size of the list. Looking up values in the map is then very efficient (approx. constant time):
map[someKey]
In this case, that takes rather more memory than needed, because both keys and values are integers and will be boxed (stored as separate objects on the heap). Also, most maps use a hash table, which takes some memory.
Since the key is (according to comments) “an ascending list starting from a random number, like 18123..19123”, in this particular case it can instead be stored in an IntArray without any boxing. As you say, array indexes start from 0, so using the key directly would need a huge array and use only the last few cells — but if you know the start key, you could simply subtract that from the array index each time.
Creating such an array would be a bit more complex, for example:
val minKey = createDatabase.minOf{ it.refKey }
val maxKey = createDatabase.maxOf{ it.refKey }
val array = IntArray(maxKey - minKey + 1)
for (row in createDatabase)
array[row.refKey - minKey] = row.random
You'd then access values with:
array[someKey - minKey]
…which is also constant-time.
Some caveats with this approach:
If createDatabase is empty, then minOf() will throw a NoSuchElementException.
If it has ‘holes’, omitting some keys inside that range, then the array will hold its default value of 0 — you can change that by using the alternative IntArray constructor which also takes a lambda giving the initial value.)
Trying to look up a value outside that range will give an ArrayIndexOutOfBoundsException.
Whether it's worth the extra complexity to save a bit of memory will depend on things like the size of the ‘database’, and how long it's in memory for; I wouldn't add that complexity unless you have good reason to think memory usage will be an issue.

How to convert two lists into a list of pairs based on some predicate

I have a data class that describes a chef by their name and their skill level and two lists of chefs with various skill levels.
data class Chef(val name: String, val level: Int)
val listOfChefsOne = listOf(
Chef("Remy", 9),
Chef("Linguini", 7))
val listOfChefsTwo = listOf(
Chef("Mark", 6),
Chef("Maria", 8))
I'm to write a function that takes these two lists and creates a list of pairs
so that the two chefs in a pair skill level's add up to 15. The challenge is to do this using only built in list functions and not for/while loops.
println(pairChefs(listOfChefsOne, listOfChefsTwo))
######################################
[(Chef(name=Remy, level=9), Chef(name=Mark, level=6)),
(Chef(name=Linguini, level=7), Chef(name=Maria, level=8))]
As I mentioned previously I'm not to use any for or while loops in my implementation for the function. I've tried using the forEach function to create a list containing all possible pairs between two lists, but from there I've gotten lost as to how I can filter out only the correct pairs.
I think the clue is in the question here!
I've tried using the forEach function to create a list containing all possible pairs between two lists, but from there I've gotten lost as to how I can filter out only the correct pairs.
There's a filter function that looks perfect for this…
To keep things clear, I'll split out a function for generating all possible pairs.  (This is my own, but bears a reassuring resemblance to part of this answer!  In any case, you said you'd already solved this bit.)
fun <A, B> Iterable<A>.product(other: Iterable<B>)
= flatMap{ a -> other.map{ b -> a to b }}
The result can then be:
val result = listOfChefsOne.product(listOfChefsTwo)
.filter{ (chef1, chef2) -> chef1.level + chef2.level == 15 }
Note that although this is probably the simplest and most readable way, it's not the most efficient for large lists.  (It takes time and memory proportional to the product of the sizes of the two lists.)  You could improve large-scale performance by using streams (which would take the same time but constant memory). But for this particular case, it might be even better to group one of the lists by level, then for each element of the other list, you could directly look up a Chef with 15 - its level.  (That would time proportional to the sum of the sizes of the two lists, and space proportional to the size of the first list.)
Here is the pretty simple naive solution:
val result = listOfChefsOne.flatMap { chef1 ->
listOfChefsTwo.mapNotNull { chef2 ->
if (chef1.level + chef2.level == 15) {
chef1 to chef2
} else {
null
}
}
}
println(result) // prints [(Chef(name=Remy, level=9), Chef(name=Mark, level=6)), (Chef(name=Linguini, level=7), Chef(name=Maria, level=8))]

Better way to create FloatArray in Kotlin

val matrix: FloatArray = emptyArray<Float>().toFloatArray()
This definitely works, it just looks pretty ugly. Is there no method to create XXXArray directly? Did I miss something?
You have three options for creating a FloatArray:
val arr1 = floatArrayOf(.1f)
val arr2 = FloatArray(12)
And, as you are doing already, emptyArray.
floatArrayOf works exactly like you'd expect; creates an array of the items with a corresponding size. It works just like arrayOf, just with a different return type.
The second one creates one defined by size. I just set the size to 12 as a demo, but you get the idea. The second one is roughly equivalent to float[] arr2 = new float[12];.
By default, it sets all the values to 0, but you can customize that with FloatArray(12) { 1f }, where 1f can be any number you want to initialize all the items in the array as. You don't need that if you just want to set it to 0 though.
You can use the floatArrayOf method to create such an array, as described in the documentation.

Get array of data based on hierarchical edges sequence in cytoscape.js

I use cytoscape.js to organize an flow of nodes that represent an tasks execution. Sometimes an task is not created hierarchicly.
At least in a visual way, edges gives the correct sequence.
I would like to get the hierarchical sequence based on the edges and list their data as an array. Each index will be dispposed as edges says so.
The image above represent a sequence based on the edges arrows. I would like to transform this edges/arrows sequence into a perfect sequence of data (array).
The cytoscape.elements().toArray() method transform visual to array, but it is delivered the same sequence of the original data.
How can it be done? Is there some method in cytoscape core?
The easiest way would be to give the nodes id's with the corresponding numbers in your sequence:
-> The first task to execute has the id 1, the second has the id 2...
After initialization you can then do a loop with n iterations (n = number of nodes in cy) and get the nodes one by one. That way you can access their information and enter this data into an array:
for (i = 0; i < cy.nodes().length; i++) {
var curr = cy.nodes("[id = '" + i + "']"); // This way you get the node with the id == i
//do stuff
array[i] = theDataYouNeed;
}
If you want the nodes to be in a hierarchy, you would have to rethink your layout. An hierarchy in cytoscape can be achieved by "directed acyclic graph" (= dagre in cytoscape).

What is the difference between var map=HashMap<Int,String>() and var map=HashMap<Int,String>(n)?

Consider n is any integer with any particular value in the range of Int data type. so What is the difference between var map=HashMap<Int,String>() and var map=HashMap<Int,String>(n) ?
The HashMap(n) overload accepts the initial capacity, which is used to initialize the HashMap with the provided capacity instead of the default one (which is rather small to reduce memory usage, 16 in JDK 8).
Use it when you know how many items you are going to put into the map, at least approximately, because it allows you to avoid overhead of re-allocating the map and re-hashing the keys as you put entries into it