Get index of each root on level wise in tree data structure - kotlin

Hey I am working on tree data structure. I want to know can we get index of each node in level wise. I below diagram represent how I want the value + index. Level A or B represent node value and index value represent index value
Node
| | |
Level A -> 1 2 3
index value-> 0 1 2
| | | | | |
| | | | | |
Leve B-> 4 5 6 7 8 9
index value-> 0 1 2 3 4 5
....// more level
How can we achieved index in each level wise. I am adding my logic how I am adding value in each level wise. Could you someone suggest how can I achieve this?
var baseNode: LevelIndex = LevelIndex()
var defaultId = "1234"
fun main() {
val list = getUnSortedDataListForLevel()
val tempHashMap: MutableMap<String, LevelIndex> = mutableMapOf()
list.forEach { levelClass ->
levelClass.levelA?.let { levelA ->
val levelOneTempHashMapNode = tempHashMap["level_a${levelA}"]
if (levelOneTempHashMapNode != null) {
if (defaultId == levelClass.id && levelOneTempHashMapNode is LevelOne) {
levelOneTempHashMapNode.defaultValue = true
}
return#let
}
val tempNode = LevelOne().apply {
value = levelA
if (defaultId == levelClass.id) {
defaultValue = true
}
}
baseNode.children.add(tempNode)
tempHashMap["level_a${levelA}"] = tempNode
}
levelClass.levelB?.let { levelB ->
val levelTwoTempHashMapNode = tempHashMap["level_a${levelClass.levelA}_level_b${levelB}"]
if (levelTwoTempHashMapNode != null) {
if (defaultId == levelClass.id && levelOneTempHashMapNode is LevelTwo) {
levelTwoTempHashMapNode.defaultValue = true
}
return#let
}
val tempNode = LevelTwo().apply {
value = levelB
if (defaultId == levelClass.id) {
defaultValue = true
}
}
val parent =
tempHashMap["level_a${levelClass.levelA}"] ?: baseNode
parent.children.add(tempNode)
tempHashMap["level_a${levelClass.levelA}_level_b${levelB}"] =
tempNode
}
levelClass.levelC?.let { levelC ->
val tempNode = LevelThree().apply {
value = levelC
if (defaultId == levelClass.id) {
defaultValue = true
}
}
val parent =
tempHashMap["level_a${levelClass.levelA}_level_b${levelClass.levelB}"]
?: baseNode
parent.children.add(tempNode)
}
}
}
open class LevelIndex(
var value: String? = null,
var children: MutableList<LevelIndex> = arrayListOf()
)
class LevelOne : LevelIndex() {
var defaultValue: Boolean? = false
}
class LevelTwo : LevelIndex() {
var defaultValue: Boolean? = false
}
class LevelThree : LevelIndex() {
var defaultValue: Boolean = false
}
UPDATE
I want index value by root level because, I have one id, I want to match that combination with that id, if that value is present then I am storing that value b true, and need to find that index value.
Node
| | |
Level A -> 1 2 3
index value-> 0 1 2
default value-> false true false
| | | | | |
| | | | | |
Leve B-> 4 5 6 7 8 9
index value-> 0 1 2 3 4 5
default value->false false true false false false
....// more level
So, Level A I'll get index 1.
For Level B I'll get index 2

I'd create a list to put the nodes at each level in order. You can recursively collect them from your tree.
val nodesByLevel = List(3) { mutableListOf<LevelIndex>() }
fun collectNodes(parent: LevelIndex) {
for (child in parent.children) {
val listIndex = when (child) {
is LevelOne -> 0
is LevelTwo -> 1
is LevelThree -> 2
// I made LevelIndex a sealed class. Otherwise you would need an else branch here.
}
nodesByLevel[listIndex] += child
collectNodes(child)
}
}
collectNodes(baseNode)
Now nodesByLevel contains three lists containing all the nodes in each layer in order.
If you just need the String values, you could change that mutableList to use a String type and use += child.value ?: "" instead, although I would make value non-nullable (so you don't need ?: ""), because what use is a node with no value?
Edit
I would move defaultValue up into the parent class so you don't have to cast the nodes to be able to read it. And I'm going to treat is as non-nullable.
sealed class LevelIndex(
var value: String = "",
val children: MutableList<LevelIndex> = arrayListOf()
var isDefault: Boolean = false
)
Then if you want to do something with the items based on their indices:
for ((layerNumber, layerList) in nodesByLevel.withIndex()) {
for((nodeIndexInLayer, node) in layerList) {
val selectedIndexForThisLayer = TODO() //with layerNumber
node.isDefault = nodeIndexInLayer == selectedIndexForThisLayer
}
}

Related

problems with index of array

I'm writing a function that allows you to remove certain numbers from an int arraylist.
My code
for (i in 1 until 4) {
divider = setDivider(i)
for(index in 0 until numbers.size){
if(index <= numbers.size){
if (numbers[index] % divider == 0 && !isDone) {
numbers.removeAt(index)
}
}else{
isDone = true
}
}
if(isDone)
break
}
the function to set the divider
fun setDivider(divider: Int): Int {
when (divider) {
1 -> return 2
2 -> return 3
3 -> return 5
4 -> return 7
}
return 8
}
I do not know why the ide is giving me the error Index 9 out of bounds for length 9.
Author explained in the comments that the goal is to remove all numbers that are divisible by 2, 3, 5 and 7.
It can be achieved much easier by utilizing ready to use functions from stdlib:
val dividers = listOf(2, 3, 5, 7)
numbers.removeAll { num ->
dividers.any { num % it == 0 }
}
It removes elements that satisfy the provided condition (is divisible) for any of provided dividers.
Also, it is often cleaner to not modify a collection in-place, but to create an entirely new collection:
val numbers2 = numbers.filterNot { num ->
dividers.any { num % it == 0 }
}

Compare multiple fields of Object to those in an ArrayList of Objects

I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}

Merge several time-series datasets into one

Given several sets of time-stamped data, how can one merge them into one?
Suppose, I have a dataset represented by the following data structure (Kotlin):
data class Data(
val ax: Double?,
val ay: Double?,
val az: Double?,
val timestamp: Long
)
ax, ay, az - accelerations over the respective axes
timestamp - unix timestamp
Now, I got three datasets: Ax, Ay, Az. Each dataset has two non-null fields: the timestamp and the acceleration over its' own axis.
Ax:
+-----+------+------+-----------+
| ax | ay | az | timestamp |
+-----+------+------+-----------+
| 0.0 | null | null | 0 |
| 0.1 | null | null | 50 |
| 0.2 | null | null | 100 |
+-----+------+------+-----------+
Ay:
+------+-----+------+-----------+
| ax | ay | az | timestamp |
+------+-----+------+-----------+
| null | 1.0 | null | 10 |
| null | 1.1 | null | 20 |
| null | 1.2 | null | 30 |
+------+-----+------+-----------+
Az:
+------+------+-----+-----------+
| ax | ay | az | timestamp |
+------+------+-----+-----------+
| null | null | 2.0 | 20 |
| null | null | 2.1 | 40 |
| null | null | 2.2 | 60 |
+------+------+-----+-----------+
What the algorithm would produce is:
+------+------+------+-----------+
| ax | ay | az | timestamp |
+------+------+------+-----------+
| 0.0 | null | null | 0 |
| 0.0 | 1.0 | null | 10 |
| 0.0 | 1.1 | 2.0 | 20 |
| 0.0 | 1.2 | 2.0 | 30 |
| 0.0 | 1.2 | 2.1 | 40 |
| 0.1 | 1.2 | 2.1 | 50 |
| 0.1 | 1.2 | 2.2 | 60 |
| 0.2 | 1.2 | 2.2 | 100 |
+------+------+------+-----------+
So in order to merge three datasets into one, I:
Put Ax, Ay and Az into one list:
val united: List<Data> = arrayListOf<Data>()
united.addAll(Ax)
united.addAll(Ay)
united.addAll(Az)
Sort the resulting list by timestamp:
united.sortBy { it.timestamp }
Copy unchanged values down the stream:
var tempAx: Double? = null
var tempAy: Double? = null
var tempAz: Double? = null
for (i in 1 until united.size) {
val curr = united[i]
val prev = united[i-1]
if (curr.ax == null) {
if (prev.ax != null) {
curr.ax = prev.ax
tempAx = prev.ax
}
else curr.ax = tempAx
}
if (curr.ay == null) {
if (prev.ay != null) {
curr.ay = prev.ay
tempAy = prev.ay
}
else curr.ay = tempAy
}
if (curr.az == null) {
if (prev.az != null) {
curr.az = prev.az
tempAz = prev.az
}
else curr.az = tempAz
}
}
Remove duplicated rows (with the same timestamp):
return united.distinctBy { it.timestamp }
The above method could be improved by merging two lists at a time, I could perhaps create a function for that.
Is there a more elegant solution to this problem? Any thoughts? Thanks.
I assume that your Data rather contains vars instead of vals (otherwise your code wouldn't work). The following is a rewrite of your function using grouped timestamps and a method, that either extracts the interested property or returns the last known value for the given property otherwise.
// your tempdata containing the default (starting) values:
val tempData = Data(0.0, 0.0, 0.0, 0L)
fun extract(dataList: List<Data>, prop: KMutableProperty1<Data, Double?>) =
// find the first non null value for the given property
dataList.firstOrNull { prop(it) != null }
// extract that property
?.let(prop)
// set the extracted value in our tempData so that it can reused if a null value is retrieved in future
?.also { prop.set(tempData, it) }
// if the above didn't return a value, use the last one set into tempData
?: prop(tempData)
val mergedData = /* your united.addAll */ (Ax + Ay + Az)
.groupBy { it.timestamp }
// your sort by timestamp
.toSortedMap()
.map {(timestamp, dataList) ->
Data(extract(dataList, Data::ax),
extract(dataList, Data::ay),
extract(dataList, Data::az),
timestamp
)
It's rather hard to come up with a better approach as your main condition (defaulting to the last resolved value) will actually force you to have your dataset sorted and to hold a (or several) temporary variable(s).
However, the benefits of this version in contrast to yours are the following:
don't bother about the indices
less duplicated code
no need to remove any duplicates from the returned list (no need to distinctBy)
while the extract-method itself might be complex, the usage of it is more readable
Maybe by refactoring the extract the whole gets more readable too.
As you also said, that you want it to be easily portable to Java, here a possible Java rewrite:
Map<Long, List<Data>> unitedList = Stream.concat(Stream.concat(Ax.stream(), Ay.stream()), Az.stream())
.collect(Collectors.groupingBy(Data::getTimestamp));
List<Data> mergedData = unitedList.keySet().stream().sorted()
.map(key -> {
List<Data> dataList = unitedList.get(key);
return new Data(extract(dataList, Data::getAx, Data::setAx),
extract(dataList, Data::getAy, Data::setAy),
extract(dataList, Data::getAz, Data::setAz),
key);
}).collect(Collectors.toList());
and the extract could then look like:
Double extract(List<Data> dataList, Function<Data, Double> getter, BiConsumer<Data, Double> setter) {
Optional<Double> relevantProperty = dataList.stream()
.map(getter)
.filter(Objects::nonNull)
.findFirst();
if (relevantProperty.isPresent()) {
setter.accept(tempData, relevantProperty.get());
return relevantProperty.get();
} else {
return getter.apply(tempData);
}
}
Basically the same mechanism.
So at the moment I am using this solution:
data class Data(
var ax: Double?,
var ay: Double?,
var az: Double?,
val timestamp: Long
)
fun mergeDatasets(Ax: List<Data>, Ay: List<Data>, Az: List<Data>): List<Data> {
val united = mutableListOf<Data>()
united.addAll(Ax)
united.addAll(Ay)
united.addAll(Az)
united.sortBy { it.timestamp }
var tempAx: Double? = null
var tempAy: Double? = null
var tempAz: Double? = null
for (i in 1 until united.size) {
val curr = united[i]
val prev = united[i-1]
if (curr.ax == null) {
if (prev.ax != null) {
curr.ax = prev.ax
tempAx = prev.ax
}
else curr.ax = tempAx
}
if (curr.ay == null) {
if (prev.ay != null) {
curr.ay = prev.ay
tempAy = prev.ay
}
else curr.ay = tempAy
}
if (curr.az == null) {
if (prev.az != null) {
curr.az = prev.az
tempAz = prev.az
}
else curr.az = tempAz
}
if (curr.timestamp == prev.timestamp) {
prev.ax = curr.ax
prev.ay = curr.ay
prev.az = curr.az
}
}
return united.distinctBy { it.timestamp }
}

Reverse Cartesian Product

Given the data set below:
a | b | c | d
1 | 3 | 7 | 11
1 | 5 | 7 | 11
1 | 3 | 8 | 11
1 | 5 | 8 | 11
1 | 6 | 8 | 11
Perform a reverse Cartesian product to get:
a | b | c | d
1 | 3,5 | 7,8 | 11
1 | 6 | 8 | 11
I am currently working with scala, and my input/output data type is currently:
ListBuffer[Array[Array[Int]]]
I have come up with a solution (seen below), but feel it could be optimized. I am open to optimizations of my approach, and completely new approaches. Solutions in scala and c# are preferred.
I am also curious if this could be done in MS SQL.
My current solution:
def main(args: Array[String]): Unit = {
// Input
val data = ListBuffer(Array(Array(1), Array(3), Array(7), Array(11)),
Array(Array(1), Array(5), Array(7), Array(11)),
Array(Array(1), Array(3), Array(8), Array(11)),
Array(Array(1), Array(5), Array(8), Array(11)),
Array(Array(1), Array(6), Array(8), Array(11)))
reverseCartesianProduct(data)
}
def reverseCartesianProduct(input: ListBuffer[Array[Array[Int]]]): ListBuffer[Array[Array[Int]]] = {
val startIndex = input(0).size - 1
var results:ListBuffer[Array[Array[Int]]] = input
for (i <- startIndex to 0 by -1) {
results = groupForward(results, i, startIndex)
}
results
}
def groupForward(input: ListBuffer[Array[Array[Int]]], groupingIndex: Int, startIndex: Int): ListBuffer[Array[Array[Int]]] = {
if (startIndex < 0) {
val reduced = input.reduce((a, b) => {
mergeRows(a, b)
})
return ListBuffer(reduced)
}
val grouped = if (startIndex == groupingIndex) {
Map(0 -> input)
}
else {
groupOnIndex(input, startIndex)
}
val results = grouped.flatMap{
case (index, values: ListBuffer[Array[Array[Int]]]) =>
groupForward(values, groupingIndex, startIndex - 1)
}
results.to[ListBuffer]
}
def groupOnIndex(list: ListBuffer[Array[Array[Int]]], index: Int): Map[Int, ListBuffer[Array[Array[Int]]]] = {
var results = Map[Int, ListBuffer[Array[Array[Int]]]]()
list.foreach(a => {
val key = a(index).toList.hashCode()
if (!results.contains(key)) {
results += (key -> ListBuffer[Array[Array[Int]]]())
}
results(key) += a
})
results
}
def mergeRows(a: Array[Array[Int]], b: Array[Array[Int]]): Array[Array[Int]] = {
val zipped = a.zip(b)
val merged = zipped.map{ case (array1: Array[Int], array2: Array[Int]) =>
val m = array1 ++ array2
quickSort(m)
m.distinct
.array
}
merged
}
The way this works is:
Loop over columns, from right to left (the groupingIndex specifies which column to run on. This column is the only one which does not have to have values equal to each other in order to merge the rows.)
Recursively group the data on all other columns (not groupingIndex).
After grouping all columns, it is assumed that the data in each group have equivalent values in every column except for the grouping column.
Merge the rows with the matching columns. Take the distinct values for each column and sort each one.
I apologize if some of this does not make sense, my brain is not functioning today.
Here is my take on this. Code is in Java but could easily be converted into Scala or C#.
I run groupingBy on all combinations of n-1 and go with the one that has the lowest count, meaning largest merge depth, so this is kind of a greedy approach. However it is not guaranteed that you will find the optimal solution, meaning minimize the number k which is np-hard to do, see link here for an explanation, but you will find a solution that is valid and do it rather fast.
Full example here: https://github.com/jbilander/ReverseCartesianProduct/tree/master/src
Main.java
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<List<Integer>> data = List.of(List.of(1, 3, 7, 11), List.of(1, 5, 7, 11), List.of(1, 3, 8, 11), List.of(1, 5, 8, 11), List.of(1, 6, 8, 11));
boolean done = false;
int rowLength = data.get(0).size(); //4
List<Table> tables = new ArrayList<>();
// load data into table
for (List<Integer> integerList : data) {
Table table = new Table(rowLength);
tables.add(table);
for (int i = 0; i < integerList.size(); i++) {
table.getMap().get(i + 1).add(integerList.get(i));
}
}
// keep track of count, needed so we know when to stop iterating
int numberOfRecords = tables.size();
// start algorithm
while (!done) {
Collection<List<Table>> result = getMinimumGroupByResult(tables, rowLength);
if (result.size() < numberOfRecords) {
tables.clear();
for (List<Table> tableList : result) {
Table t = new Table(rowLength);
tables.add(t);
for (Table table : tableList) {
for (int i = 1; i <= rowLength; i++) {
t.getMap().get(i).addAll(table.getMap().get(i));
}
}
}
numberOfRecords = tables.size();
} else {
done = true;
}
}
tables.forEach(System.out::println);
}
private static Collection<List<Table>> getMinimumGroupByResult(List<Table> tables, int rowLength) {
Collection<List<Table>> result = null;
int min = Integer.MAX_VALUE;
for (List<Integer> keyCombination : getKeyCombinations(rowLength)) {
switch (rowLength) {
case 4: {
Map<Tuple3<TreeSet<Integer>, TreeSet<Integer>, TreeSet<Integer>>, List<Table>> map =
tables.stream().collect(Collectors.groupingBy(t -> new Tuple3<>(
t.getMap().get(keyCombination.get(0)),
t.getMap().get(keyCombination.get(1)),
t.getMap().get(keyCombination.get(2))
)));
if (map.size() < min) {
min = map.size();
result = map.values();
}
}
break;
case 5: {
//TODO: Handle n = 5
}
break;
case 6: {
//TODO: Handle n = 6
}
break;
}
}
return result;
}
private static List<List<Integer>> getKeyCombinations(int rowLength) {
switch (rowLength) {
case 4:
return List.of(List.of(1, 2, 3), List.of(1, 2, 4), List.of(2, 3, 4), List.of(1, 3, 4));
//TODO: handle n = 5, n = 6, etc...
}
return List.of(List.of());
}
}
Output of tables.forEach(System.out::println)
Table{1=[1], 2=[3, 5, 6], 3=[8], 4=[11]}
Table{1=[1], 2=[3, 5], 3=[7], 4=[11]}
or rewritten for readability:
a | b | c | d
--|-------|---|---
1 | 3,5,6 | 8 | 11
1 | 3,5 | 7 | 11
If you were to do all this in sql (mysql) you could possibly use group_concat(), I think MS SQL has something similar here: simulating-group-concat or STRING_AGG if SQL Server 2017, but I think you would have to work with text columns which is a bit nasty in this case:
e.g.
create table my_table (A varchar(50) not null, B varchar(50) not null,
C varchar(50) not null, D varchar(50) not null);
insert into my_table values ('1','3,5','4,15','11'), ('1','3,5','3,10','11');
select A, B, group_concat(C order by C) as C, D from my_table group by A, B, D;
Would give the result below, so you would have to parse and sort and update the comma separated result for any next merge iteration (group by) to be correct.
['1', '3,5', '3,10,4,15', '11']

How to upate a record that has another record within it?

I have a record :
type alias Point = {x : Int, y : Int}
I have another record like this :
type alias SuperPoint = {p : Point, z : Int}
p = Point 5 10
sp = SuperPoint p 15
Now if I need to update SuperPoint.z I can do this :
{sp | z = 20}
How do I update SuperPoint.Point?
sp2 =
let
p2 = { p | x = 42 }
in
{ sp | p = p2 }
Right now you have four ways to update:
Two of them you can find below in code
third is using Monocle.
fourth: Re-structure your model with Dictionaries and handle the update in a proper generic way
A the end there is also a code which doesn't work, but could be useful, if that worked that way
Example in elm-0.18
import Html exposing (..)
model =
{ left = { x = 1 }
}
updatedModel =
let
left =
model.left
newLeft =
{ left | x = 10 }
in
{ model | left = newLeft }
updateLeftX x ({ left } as model) =
{ model | left = { left | x = x } }
updatedModel2 =
updateLeftX 11 model
main =
div []
[ div [] [ text <| toString model ]
, div [] [ text <| toString updatedModel ]
, div [] [ text <| toString updatedModel2 ]
]
Examples from https://groups.google.com/forum/#!topic/elm-discuss/CH77QbLmSTk
-- nested record updates are not allowed
-- https://github.com/elm-lang/error-message-catalog/issues/159
updatedModel3 =
{ model | left = { model.left | x = 12 } }
-- The .field shorthand does not for the update syntax
-- https://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/
updateInt : Int -> (data -> Int) -> data -> data
updateInt val accessor data =
{ data | accessor = val }
updatedModel4 =
{ model | left = updateInt 13 .x model.left }