Kotlin - List within a List filtering - list

I have those data classes:
data class RouteType(
#SerializedName("type")
val type: String,
#SerializedName("items")
val items: List<RouteItem>)
data class RouteItem(
#SerializedName("id")
val id: String,
#SerializedName("route")
private val route: List<DoubleArray>)
I want to filter list of RouteType by type and filter list of RouteItem in it by id.
My code now:
// val filter: HashMap<String, List<String>>
val result = routeTypes // List<RouteType>
.filter { it.type in filter.keys }
.map {
routeType -> routeType.items.filter { it.id in filter[routeType.type]!! }
}
How to make .map return list with filtered list in it? Or maybe there's another way?
EDIT
Thanks, but flatmap not exactly what I need, I think. flatmap returns nested list(List<RouteItem>), but I want List<RouteType>.
I got it by this code:
val result = routeTypes
.filter { it.type in filter.keys }
.map {
routeType -> RouteType(
routeType.type,
routeType.items.filter { it.id in filter[routeType.type]!! })
}
Is there another way to get it?

Since your data is immutable (that's a good thing) you need to copy it while filtering. Use copy to make it more extensible:
val result = routeTypes
.filter { it.type in filter.keys }
.map { it.copy(items = it.items.filter { it.id in filter[routeType.type]!! }) }

You can use flatMap for this, it works as map, but merges all your mapped collections to one:
val result = routeTypes // List<RouteType>
.filter { it.type in filter.keys }
.flatMap {
routeType -> routeType.items.filter { it.id in filter[routeType.type]!! }
}

Related

performance implications of holding state in large data classes and updating it using copy()

In order to store state in jetpack compose I have so far used the the following pattern:
private val _largeDataClass:MutableState<LargeDataClass> = mutableStateOf(LargeDataClass())
val largeDataClass :State<LargeDataClass> = _largeDataClass
then I display some or all of the properties of this class in my composition. When the user changes a property of this data class I need to update the state and I do it in the following way:
fun onUserEvent(somePropertyChange:String){
_largeDataClass.value=largeDataClass.value.copy(someProperty = somePropertyChange)
}
I got this approach from the following post. It works and has the benefit of keeping my codebase relatively small (as there might be 20+ different properties in LargeDataClass that I dont need to declare individually as mutable state) BUT, if I am not mistaken, following this approach will trigger the recomposition of my entire screen even if the user just types a single letter in one of my many TextFields. As all my composables display some property of largeDataClass and they have just been notified that its value has changed.
My first question is wether I am right in this last assumption. Will my current way of holding state negatively affect my apps performance because I am forcing the screen to redraw itself completely constantly? Or are there some optimizations, that I'm unaware of, in compose that prevent this from happening and render my appoach safe?
my second question: I would really love it if there was a way of turning a data class, say:
data class Student(
val key: String = "s0",
val firstName: String = "",
val lastName: String = "")
into an equivalent state holder class (something similar to the following)
class StudentState(s:Student){
val key= mutableStateOf(s:Key),
val firstName= mutableStateOf(s.firstName),
val lastName= mutableStateOf(s.lastName)}
(Ideally without having to explicitly write such a class myself every time) Does this exist already? is there a way of using reflection or the like to achieve this for a generic data class?
I am still learning to deal with state in jetpack compose and I want to get it right. It seems to me that tracking the properties of my data classes individually either in the ViewModel or in a State Holder class is the right thing to do, but on the other hand this makes my code a lot longer and it just feels like I am doing a lot of stuff twice and my code becomes less readable and maintainable. Any insights are much appreciated
You could use reflection to create mutableStates for the members of any class like so:
fun createStateMap(baseObject: Any) = with(baseObject::class.memberProperties) {
map { it.name }.zip(map { mutableStateOf(it.getter.call(baseObject)) }).toMap()
}
And then use it like this:
class MyViewModel : ViewModel() {
private val student = Student(firstName = "John", lastName = "Doe")
val studentStateMap = createStateMap(student)
}
#Composable
fun MyComposable(viewModel: MyViewModel) {
val student = viewModel.studentStateMap
Button(
onClick = { student["firstName"]?.value = "Jack" }
) {
Text(text = student["firstName"]?.value.toString())
}
}
I wouldn't use it, it's not typesafe, it's messy and ugly, but it's possible.
An Annotation to store data class #AsState
Well I am still not sure about wether it is fine to simply .copy(changedValue = "...") a large data class or if this is inefficient because it triggers unecessary recompositions.
In any case just to be safe (and cause I think its cleaner) I wrote an AnnotationProcessor that takes my data classes and creates both a mutable and immutable version of the class holding all properties as state. It supports both lists and maps but is shallow (meaning that it wont repeat the same process for nested classes). Here an example of the class with the annotation and the result.
please let me know if you consider this to be usefull in order to track state more cleanly when a data class is displayed/edited in a composable (and also if you dont)
The original class
#AsState
data class Test(val name:String, val age:Int, val map:HashMap<String,Int>, val list:ArrayList<String>)
The mutable verion of the class with a custonm constructor and rootClass getter
public class TestMutableState {
public val name: MutableState<String>
public val age: MutableState<Int>
public val map: SnapshotStateMap<String, Int>
public val list: SnapshotStateList<String>
public constructor(rootObject: Test) {
this.name=mutableStateOf(rootObject.name)
this.age=mutableStateOf(rootObject.age)
this.map=rootObject.map.map{Pair(it.key,it.value)}.toMutableStateMap()
this.list=rootObject.list.toMutableStateList()
}
public fun getTest(): Test = Test(name = this.name.value,
age = this.age.value,
map = HashMap(this.map),
list = ArrayList(this.list),
)
}
The immutable version that can be public in the ViewModel
public class TestState {
public val name: State<String>
public val age: State<Int>
public val map: SnapshotStateMap<String, Int>
public val list: SnapshotStateList<String>
public constructor(mutableObject: TestMutableState) {
this.name=mutableObject.name
this.age=mutableObject.age
this.map=mutableObject.map
this.list=mutableObject.list
}
}
TL;DR
Next I am pasting the source code for my annotation processor so you can implement it. I basically followed this article and implemented some of my own changes based on arduous googling. I might make this a module in the future so that other people can more easily implement this in their projects i there is any interest:
Annotation class
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.SOURCE)
public annotation class AsState
Annotation processor
#AutoService(Processor::class)
class AnnotationProcessor : AbstractProcessor() {
companion object {
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
}
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(AsState::class.java.name)
}
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(AsState::class.java)
.forEach {
if (it.kind != ElementKind.CLASS) {
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated")
return true
}
processAnnotation(it)
}
return false
}
#OptIn(KotlinPoetMetadataPreview::class, com.squareup.kotlinpoet.DelicateKotlinPoetApi::class)
private fun processAnnotation(element: Element) {
val className = element.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(element).toString()
val kmClass = (element as TypeElement).toImmutableKmClass()
//create vessel for mutable state class
val mutableFileName = "${className}MutableState"
val mutableFileBuilder= FileSpec.builder(pack, mutableFileName)
val mutableClassBuilder = TypeSpec.classBuilder(mutableFileName)
val mutableConstructorBuilder= FunSpec.constructorBuilder()
.addParameter("rootObject",element.asType().asTypeName())
var helper="return ${element.simpleName}("
//create vessel for immutable state class
val stateFileName = "${className}State"
val stateFileBuilder= FileSpec.builder(pack, stateFileName)
val stateClassBuilder = TypeSpec.classBuilder(stateFileName)
val stateConstructorBuilder= FunSpec.constructorBuilder()
.addParameter("mutableObject",ClassName(pack,mutableFileName))
//import state related libraries
val mutableStateClass= ClassName("androidx.compose.runtime","MutableState")
val stateClass=ClassName("androidx.compose.runtime","State")
val snapshotStateMap= ClassName("androidx.compose.runtime.snapshots","SnapshotStateMap")
val snapshotStateList=ClassName("androidx.compose.runtime.snapshots","SnapshotStateList")
fun processMapParameter(property: ImmutableKmValueParameter) {
val clName =
((property.type?.abbreviatedType?.classifier) as KmClassifier.TypeAlias).name
val arguments = property.type?.abbreviatedType?.arguments?.map {
ClassInspectorUtil.createClassName(
((it.type?.classifier) as KmClassifier.Class).name
)
}
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
arguments?.let {
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateMap.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
arguments?.let {
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateMap.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
helper = helper.plus("${paramName} = ${paramClass.simpleName}(this.${paramName}),\n")
mutableConstructorBuilder
.addStatement("this.${paramName}=rootObject.${paramName}.map{Pair(it.key,it.value)}.toMutableStateMap()")
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
fun processListParameter(property: ImmutableKmValueParameter) {
val clName =
((property.type?.abbreviatedType?.classifier) as KmClassifier.TypeAlias).name
val arguments = property.type?.abbreviatedType?.arguments?.map {
ClassInspectorUtil.createClassName(
((it.type?.classifier) as KmClassifier.Class).name
)
}
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
arguments?.let {
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateList.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
arguments?.let {
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateList.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
helper = helper.plus("${paramName} = ${paramClass.simpleName}(this.${paramName}),\n")
mutableConstructorBuilder
.addStatement("this.${paramName}=rootObject.${paramName}.toMutableStateList()")
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
fun processDefaultParameter(property: ImmutableKmValueParameter) {
val clName = ((property.type?.classifier) as KmClassifier.Class).name
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
mutableStateClass.parameterizedBy(paramClass), KModifier.PUBLIC
).build()
)
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
stateClass.parameterizedBy(paramClass),
KModifier.PUBLIC
).build()
)
helper = helper.plus("${paramName} = this.${paramName}.value,\n")
mutableConstructorBuilder
.addStatement(
"this.${paramName}=mutableStateOf(rootObject.${paramName}) "
)
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
for (property in kmClass.constructors[0].valueParameters) {
val javaPackage = (property.type!!.classifier as KmClassifier.Class).name.replace("/", ".")
val javaClass=try {
Class.forName(javaPackage)
}catch (e:Exception){
String::class.java
}
when{
Map::class.java.isAssignableFrom(javaClass) ->{ //if property is of type map
processMapParameter(property)
}
List::class.java.isAssignableFrom(javaClass) ->{ //if property is of type list
processListParameter(property)
}
else ->{ //all others
processDefaultParameter(property)
}
}
}
helper=helper.plus(")") //close off method
val getRootBuilder= FunSpec.builder("get$className")
.returns(element.asClassName())
getRootBuilder.addStatement(helper.toString())
mutableClassBuilder.addFunction(mutableConstructorBuilder.build()).addFunction(getRootBuilder.build())
stateClassBuilder.addFunction(stateConstructorBuilder.build())
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
val mutableFile = mutableFileBuilder
.addImport("androidx.compose.runtime", "mutableStateOf")
.addImport("androidx.compose.runtime","toMutableStateMap")
.addImport("androidx.compose.runtime","toMutableStateList")
.addType(mutableClassBuilder.build())
.build()
mutableFile.writeTo(File(kaptKotlinGeneratedDir))
val stateFile = stateFileBuilder
.addType(stateClassBuilder.build())
.build()
stateFile.writeTo(File(kaptKotlinGeneratedDir))
}
}
gradle annotation
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
gradle processor
plugins {
id 'kotlin'
id 'kotlin-kapt'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotations')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10"
// https://mvnrepository.com/artifact/com.squareup/kotlinpoet
implementation 'com.squareup:kotlinpoet:1.10.2'
implementation "com.squareup:kotlinpoet-metadata:1.7.1"
implementation "com.squareup:kotlinpoet-metadata-specs:1.7.1"
implementation "com.google.auto.service:auto-service:1.0.1"
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-metadata-jvm
implementation "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.2"
implementation 'org.json:json:20211205'
kapt "com.google.auto.service:auto-service:1.0.1"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

return a new list from function in a for loop to pass in the updated list

I have a method that take a list as a parameter that performs some operation on it and returns the new list. However, in my for..loop I would to keep passing in the updated list until the for..loop has completed.
Is there a way to do this?
fun main(args: Array<String>) {
val listOfSeatRows = (1..127).toList()
// Just loop until all the listOfPass has completed.
listOfPass.forEach { seatPass ->
val seat = Seat.valueOf(seatPass.toString())
// I want to pass in the new updated list not the same list
getListOfSeatRows(listOfSeatRows, seat)
}
}
This method takes the list and return a updated list. However, in the for..loop above I would like to pass in the list that is returned from this method
private fun getListOfSeatRows(listOfSeat: List<Int>, seatPosition: Seat): List<Int> {
return when(seatPosition) {
Seat.F, Seat.L -> {
listOfSeat.windowed(listOfSeat.count() / 2).first()
}
Seat.B, Seat.R -> {
listOfSeat.windowed(listOfSeat.count() / 2).last()
}
}
}
enum class Seat(seat: Char) {
F('F'),
B('B'),
L('L'),
R('R')
}
Either you mutate the variable:
fun main(args: Array<String>) {
var listOfSeatRows = (1..127).toList()
// Just loop until all the listOfPass has completed.
listOfPass.forEach { seatPass ->
val seat = Seat.valueOf(seatPass.toString())
// I want to pass in the new updated list not the same list
listOfSeatRows = getListOfSeatRows(listOfSeatRows, seat)
}
}
or you mutate the list:
fun main(args: Array<String>) {
var listOfSeatRows = (1..127).toMutableList()
// Just loop until all the listOfPass has completed.
listOfPass.forEach { seatPass ->
val seat = Seat.valueOf(seatPass.toString())
// I want to pass in the new updated list not the same list
reduceListOfSeatRows(listOfSeatRows, seat)
}
}
private fun reduceListOfSeatRows(listOfSeat: MutableList<Int>, seatPosition: Seat) {
val half = listOfSeat.size / 2
when(seatPosition) {
Seat.F, Seat.L -> {
while (listOfSeat.size > half) listOfSeat.removeLast()
}
Seat.B, Seat.R -> {
while (listOfSeat.size > half) listOfSeat.removeFirst()
}
}
}
If you stick with mutating the property, your function can be simplified (and avoid wasteful creation of multiple intermediate lists) using take/takeLast:
private fun getListOfSeatRows(listOfSeat: List<Int>, seatPosition: Seat): List<Int> {
return when(seatPosition) {
Seat.F, Seat.L -> {
listOfSeat.take(listOfSeat.size / 2)
}
Seat.B, Seat.R -> {
listOfSeat.takeLast(listOfSeat.size / 2)
}
}
}
recursion
maybe that's will help with some enhancement depending on your code:
var ss = 1
val listOfPass = listOf<Char>('F', 'L','B','R')
fun main(args: Array<String>) {
val listOfSeatRows = (1..127).toList()
val answer = getListOfSeatRows(
listOfSeatRows,
listOfSeatRows.count() / 2,
Seat.valueOf(listOfPass[0].toString())
)
println(answer)
}
private fun getListOfSeatRows(listOfSeat: List<Int>, count: Int, seatPosition: Seat): List<Int> {
val tempList: List<Int> = when (seatPosition) {
Seat.F, Seat.L -> {
listOfSeat.windowed(count).first()
}
Seat.B, Seat.R -> {
listOfSeat.windowed(count).last()
}
else -> listOfSeat
}
if(count == 0 || count == 1) return listOfSeat
if (listOfPass.size > ss) {
val seat = Seat.valueOf(listOfPass[ss++].toString())
return getListOfSeatRows(tempList, count / 2, seat)
}
return listOfSeat
}

How can I distinct a complex object list in DART

I have one list of complex object. How can I distinct the list using their IDs?
I cant use toSet and similars, because the hashcode from the objects all are diferent.
1) Vanilla Dart
Loop through the list, adding IDs to a set as you go. Whenever you add an ID to the set that didn't already exist, add that element to a new list of distinct values.
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var idSet = <String>{};
var distinct = <Data>[];
for (var d in list) {
if (idSet.add(d.id)) {
distinct.add(d);
}
}
}
class Data {
Data(this.id);
final String id;
}
2) Packages
Several packages exist that expand on default the Iterable utility methods, such as flinq or darq. They add a distinct method you can call to easily get a list of unique members of a list based on some property of the members.
import 'package:darq/darq.dart';
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var distinct = list.distinct((d) => d.id).toList();
}
(Disclaimer, I am the maintainer of darq.)
Try to use this extension:
extension IterableExtension<T> on Iterable<T> {
Iterable<T> distinctBy(Object getCompareValue(T e)) {
var result = <T>[];
this.forEach((element) {
if (!result.any((x) => getCompareValue(x) == getCompareValue(element)))
result.add(element);
});
return result;
}
}
Using:
var distinctList = someList.distinctBy((x) => x.oid);
Or you can use a hash there.

Swift: Finding an Object Property via regex

Target: The following function shall iterate over an array of objects and check a specific property of all objects. This property is a string and shall be matched with a user input via regex. If there's a match the object shall be added to an array which will further be passed to another function.
Problem: I don't know how to set up regex in Swift 3. I'm rather new in Swift at all, so an easily understandable solution would be very helpful :)
How it currently looks like:
func searchItems() -> [Item] {
var matches: [Item] = []
if let input = readLine() {
for item in Storage.storage.items { //items is a list of objects
if let query = //regex with query and item.name goes here {
matches.append(item)
}
}
return matches
} else {
print("Please type in what you're looking for.")
return searchItems()
}
}
This is what Item looks like (snippet):
class Item: CustomStringConvertible {
var name: String = ""
var amount: Int = 0
var price: Float = 0.00
var tags: [String] = []
var description: String {
if self.amount > 0 {
return "\(self.name) (\(self.amount) pcs. in storage) - \(price) €"
} else {
return "\(self.name) (SOLD OUT!!!) - \(price) €"
}
}
init(name: String, price: Float, amount: Int = 0) {
self.name = name
self.price = price
self.amount = amount
}
}
extension Item: Equatable {
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.name == rhs.name
}
}
Solved. I just edited this post to get a badge :D
For the purpose of letting the answer to be generic and clear, I will assume that the Item model is:
struct Item {
var email = ""
}
Consider that the output should be a filtered array of items that contains items with only valid email.
For such a functionality, you should use NSRegularExpression:
The NSRegularExpression class is used to represent and apply regular
expressions to Unicode strings. An instance of this class is an
immutable representation of a compiled regular expression pattern and
various option flags.
According to the following function:
func isMatches(_ regex: String, _ string: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: regex)
let matches = regex.matches(in: string, range: NSRange(location: 0, length: string.characters.count))
return matches.count != 0
} catch {
print("Something went wrong! Error: \(error.localizedDescription)")
}
return false
}
You can decide if the given string does matches the given regex.
Back to the example, consider that you have the following array of Item Model:
let items = [Item(email: "invalid email"),
Item(email: "email#email.com"),
Item(email: "Hello!"),
Item(email: "example#example.net")]
You can get the filtered array by using filter(_:) method:
Returns an array containing, in order, the elements of the sequence
that satisfy the given predicate.
as follows:
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailItems = items.filter {
isMatches(emailRegex, $0.email)
}
print(emailItems) // [Item(email: "email#email.com"), Item(email: "example#example.net")]
Hope this helped.
You can do the same with filter function
let matches = Storage.storage.items.filter({ $0.yourStringPropertyHere == input })

scala list addall method

This is my java code,
public void test( List<Map> courses)
{
....
List<Map> data = (List<Map>) response.getBody();
courses.addAll(data);
pageNo = pagination(response.getHeaders());
if(pageNo!=null)
{
params.put("pageNo", pageNo);
pAccountCourses(params, courses);
}
}
How to convert it into scala List[AccountCourses] , so that i can add courseList into accountCourseslist
def test(courseList: java.util.ArrayList[AccountCourses]) {
......
//getting json data
var pageNo: String = null
val body = response.body
val json = parse(body)
var accountCourseslist = json.extract[java.util.ArrayList[AccountCourses]]
accountCourseslist.addAll(courseList)
if (pageNo != null) {
params.put("pageNo", pageNo);
test(accountCourseslist);
}
}
case class AccountCourses(id: Int) //case class
how to perform addAll operation of list in scala?
How to convert java.util.ArrayList[AccountCourses] to scala list?
use scala.collection.JavaConverters._ :
import scala.collection.JavaConverters._
val javaList = new java.util.ArrayList[Int]()
val scalaList = javaList.asScala
val scalaImmutableList = scalaList.toList // will return immutable copy
How to perform addAll operation of list in scala?
Use ++= if it is mutable collection or ++ on immutable list:
scalaList ++= List(1,2,3,4,5) // will also update javaList
val result = scalaImmutableList ++ List(1,2,3,4,5) // will return new copy