How to get max double in list?? With only one output, using Kotlin - kotlin

I have tried using .maxBy .max() and collection.Max and I have only been able to print with it stating every element is max
val fileName = "src/products.txt"
var products = HashMap<Int, Pair<String, Double>>()
var inputFD = File(fileName).forEachLine {
var pieces = it.split(",")
println("Item# Description Price")
println("----- ------------- ------")
for ( (pro,ducts) in products.toSortedMap() ) {
var pax = mutableListOf(ducts).maxBy { it -> it.second }
var highest = listOf<Double>(ducts.second).max()
println("The highest priced record is ${highest}")
}
the file is set up like this (111, shoe, 9.99)
output looks like this
The highest priced record is [(pants, 89.99)]
The highest priced record is [(shoes, 49.99)]

You are trying to print the value within the for-loop, hence it is printing it for every product. Also the variable is initialized everytime in the loop, so every value would be max.
Here is the right approach. Note that you can solve it without using mutable variables.
val fileName = "src/products.txt"
val products = File(fileName).readLines() //read all lines from file to a list
.map { it.split(",") } // map it to list of list of strings split by comma
.map { it[0] to it[1].toDouble() } // map each product to its double value in a Pair
.toMap() // convert list of Pairs to a Map
println("Item# Description Price")
println("----- ------------- ------")
products.keys.forEachIndexed { index, desc ->
println("$index\t$desc\t${products[desc]}")
}
println("The highest priced record is ${products.maxBy { it.value }}")

Related

Convert String into list of Pairs: Kotlin

Is there an easier approach to convert an Intellij IDEA environment variable into a list of Tuples?
My environment variable for Intellij is
GROCERY_LIST=[("egg", "dairy"),("chicken", "meat"),("apple", "fruit")]
The environment variable gets accessed into Kotlin file as String.
val g_list = System.getenv("GROCERY_LIST")
Ideally I'd like to iterate over g_list, first element being ("egg", "dairy") and so on.
And then ("egg", "dairy") is a tuple/pair
I have tried to split g_list by comma that's NOT inside quotes i.e
val splitted_list = g_list.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex()).toTypedArray()
this gives me first element as [("egg", second element as "dairy")] and so on.
Also created a data class and tried to map the string into data class using jacksonObjectMapper following this link:
val mapper = jacksonObjectMapper()
val g_list = System.getenv("GROCERY_LIST")
val myList: List<Shopping> = mapper.readValue(g_list)
data class Shopping(val a: String, val b: String)
You can create a regular expression to match all strings in your environmental variable.
Regex::findAll()
Then loop through the strings while creating a list of Shopping objects.
// Raw data set.
val groceryList: String = "[(\"egg\", \"dairy\"),(\"chicken\", \"meat\"),(\"apple\", \"fruit\")]"
// Build regular expression.
val regex = Regex("\"([\\s\\S]+?)\"")
val matchResult = regex.findAll(groceryList)
val iterator = matchResult.iterator()
// Create a List of `Shopping` objects.
var first: String = "";
var second: String = "";
val shoppingList = mutableListOf<Shopping>()
var i = 0;
while (iterator.hasNext()) {
val value = iterator.next().value;
if (i % 2 == 0) {
first = value;
} else {
second = value;
shoppingList.add(Shopping(first, second))
first = ""
second = ""
}
i++
}
// Print Shopping List.
for (s in shoppingList) {
println(s)
}
// Output.
/*
Shopping(a="egg", b="dairy")
Shopping(a="chicken", b="meat")
Shopping(a="apple", b="fruit")
*/
data class Shopping(val a: String, val b: String)
Never a good idea to use regex to match parenthesis.
I would suggest a step-by-step approach:
You could first match the name and the value by
(\w+)=(.*)
There you get the name in group 1 and the value in group 2 without caring about any subsequent = characters that might appear in the value.
If you then want to split the value, I would get rid of start and end parenthesis first by matching by
(?<=\[\().*(?=\)\])
(or simply cut off the first and last two characters of the string, if it is always given it starts with [( and ends in )])
Then get the single list entries from splitting by
\),\(
(take care that the split operation also takes a regex, so you have to escape it)
And for each list entry you could split that simply by
,\s*
or, if you want the quote character to be removed, use a match with
\"(.*)\",\s*\"(.*)\"
where group 1 contains the key (left of equals sign) and group 2 the value (right of equals sign)

How can use filter and contains with multi-ArrayList in Kotlin so I only have elements which match the condition?

I have a class called Person
data class Person(
val id: Int,
val name: String
)
data class IDs(
val id : Int,
val active : Boolean )
and an array list that has numbers of ids and another list of Persons
val myStu = listOf<Person>(Person(1, "Name_1"), Person(2, "Name_2"), Person(3, "Name_3"))
var ids = listOf<IDs>(IDs(1,false),IDs(2,true),IDs(3,true))
var newIds = listOf<Int>(2,3,4,6)
First I want to apply two actions to the myStu, first is to have a list that include all the items from myStu that his id matches the id in the IDS and only if the active is true
myStu or the new list will have the values
Person(2, "Name_2"), Person(3, "Name_3"))
Then do action two , I need to add a new item to the new list that their id does not exist in the newIds , in another word we will add a new person Person(4,"None") and (6,"None) , 4 and 6 values come from newIds list
the final output will be :
id= 2 name = "Name_2", id= 3 name = "Name_3", id= 4 name = "None" , id =6 name="None"
I want to write the code with filter , I failed with first step because I don't know how to use contains() with the list inside the filter
val newArr = myStu.filter {
ids.contains(it.id)
}
The "easiest" way of doing that would be to use filter directly, there's no need for contains. If we were to use contains, then we would need to also search for which element contained the id, in order to get the status. We can just do a .any() to do both at the same time.
V1
val activeStu = myStu.filter { person -> ids.any { it.id == person.id && it.active } }
val result = newIds.map { newId ->
activeStu.find { it.id == newId } ?: Person(id = newId, name = "None")
}
Another method, that might work a bit better if we have big lists, would be to first transform the IDs list into a map. That way the second part of our code is a bit more efficient, since there is no search involved.
V2
val idsMap = ids.associate { it.id to it.active }
val activeStu = myStu.filter { idsMap[it.id] ?: false }
//the creation of the result list is the same
Version without creating 2 new lists. This works, but it might be quite ineficient processing wise, and also harder to understand what is going on IMO.
V3
val result = newIds.map { newId ->
//try to find an IDs with the current newId and status as true
when (ids.find { it.id == newId }?.active) {
//if found, then find the corresponding Person
true -> myStu.find { it.id == newId } ?: Person(newId, "None") // if this happens, it means that an IDs with status true existed and no Person had that id. Not sure what you want in this scenario, this version creates one of the "none" persons.
//if not found, then create a new one
else -> Person(newId, "None")
}
}
Note: depending on what version of kotlin you have, you might have to change the when statement to this:
when (ids.find { it.id == newId }?.active == true)
Since I think I remember that null didn't used to be treated as false in old versions (I've run this with version 1.4.20).
Btw, you can also use this version with the idsMap from V2, just replace the when(...) with when(idsMap[newId] or when(idsMap[newId] == true) depending on the kotlin version.

Combining Two List in Kotlin with Index

There is a data class as fruits.
data class Fruits(
val code: String, //Unique
val name: String
)
The base list indexed items with boolean variable is as below.
val indexList: MutableList<Boolean> = MutableList(baseFruitList.size) { false }
Now the Favourite Indexed list is as below
val favList: MutableList<Boolean> = MutableList(favFruitList.size) { true}
I want a combined full list which basically has the fav item indicated as true.
Ex:
baseFruitList = {[FT1,apple],[FT2,grapes],[FT3,banana],[FT4,mango],[FT5,pears]}
favList = {[FT2,grapes],[FT4,mango]}
The final index list should have
finalIndexed = {false,true,false,true,false}
How can we achieve in Kotlin, without iterating through each element.
You can do
val finalIndexed = baseFruitList.map { it in favList }
assuming, like #Tenfour04 is asking, that name is guaranteed to be a specific value (including matching case) for a specific code (since that combination is how a data class matches another, e.g. for checking if it's in another list)
If you can't guarantee that, this is safer:
val finalIndexed = baseFruitList.map { fruit ->
favList.any { fav.code == fruit.code }
}
but here you have to iterate over all the favs (at least until you find a match) looking to see if one has the code.
But really, if code is the unique identifier here, why not just store those in your favList?
favList = listOf("FT2", "FT4") // or a Set would be more efficient, and more correct!
val finalIndexed = baseFruitList.map { it.code in favList }
I don't know what you mean about "without iterating through each element" - if you mean without an explicit indexed for loop, then you can use these simple functions like I have here. But there's always some amount of iteration involved. Sets are always an option to help you minimise that

How to filter elements in one list by a property value not present in elements in another list?

I have the following code snippet
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val result = freshNews.filter {fresh -> filter(cachedNews, fresh)}
private fun filter(cached: List<News>, fresh: News): Boolean {
cached.forEach { cachedItem ->
if (cachedItem.url == fresh.url) return true
}
return false }
When the code runs if cachedItem.url == fresh.url the list is filtered and the result is a list where the urls of the two lists are identical. However when i reverse equality like so cachedItem.url != fresh.url the list is not filtered at all. The sequence of execution changes.
When using the == sign, the first item of freshNews is compared with the first Item of cachedNews after that the secondItem of freshNews is compared with secondItem of cachedNews and so on.
When I use the != sign the all items of freshNews are compared against only the firstItem of cachedNews ??
Am I missing something or is my code just wrong?
I'm not sure what the specific problem is because your approach is quite confusing. Your custom filter function is actually more like a contains function.
What might be useful is to:
Extract the cached URLs to a set
Filter the new results by URLs that are not in the set.
fun main() {
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val cachedUrls = cachedNews.map { it.url }.toSet()
val result = freshNews.filterNot { cachedUrls.contains(it.url) }
println(result)
}
Result:
[News(id=1, url=https://123)]

Print values from HashMap

Currently, I am trying to write a method to locate the highest value product in my hashmap.
I am looking for a push in the right direction if that is possible. I appreciate any help that might be pointed my way. Thank you.
In order to be consistent, I would suggest you using horizontal tab \t as the divider.
To align the text in the right columns, I would also create a method which adds some whitespaces right after the product name (e.g jeans). The number of input whitespaces should be equal to the subtraction of the column title (e.g Description) and the product name (e.g jeans).
Try this code:
fun addWhitespaces(input: String, referencedWord: String = "Description"): String
{
var ws = ""
(0..(referencedWord.length - input.length)).map { ws += " " }
return ws
}
The method addWhitespaces() is called within the last print.
products.maxBy { it.value.second } ?.let { (key, value) ->
println("The highest priced product is: ")
println()
println("Item#\tDescription\tPrice")
println("-----\t-----------\t-----")
println("$key \t${value.first}${addWhitespaces(value.first)}\t${value.second}")
}
Also as Ali Gelenler mentioned, you should check for the null condition as the result of maxBy() may be null.
maxBy returns nullable Map.Entry so you need to handle nullable result. You could for example use safe call with ? following by let as below.
products.maxBy { it.value.second }?.let {
val (key, value) = it
println("The highest priced product is: ")
println()
println("Item# Description Price")
println("----- ------------- -------")
println("$key ${value.first} ${value.second}")
}
Your print statement is the default format from the map entry. Change your print statement to something like this:
println("${highestPricedProduct.key}\t${highestPricedProduct.value.first}\t${highestPricedProduct.value.second}")