Combine two variables in one request using Rx - android

I want to combine company and workFor variables into one stream, but I don't know how. I tried to use switchMap, zip, merge and concatMapIterable but nothing worked. Or I did something wrong..
My data model:
data class UserCompany(
#field:Json(name = "user") val user: User,
#field:Json(name = "company") val company: Company,
#field:Json(name = "workFor") val workFor: List<Company>
)
And my current code:
authApi.getCompany(getJwt()) //here I get data that looks like the above model
.subscribeOn(Schedulers.io())
.flatMapIterable {
return#flatMapIterable it.data.workFor.toMutableList().add(it.data.company) //error Type mismatch
},
//should returns stream of company and workFor
How to combine two variables from one request using Rx?
Edit, better example.
Let's say we have:
data class Pet(val name: String)
data class PetResult(
val myPet: Pet, //dog
val otherPet: List<Pet> //cat, bird, cow
)
And i wannt to get something like this:
authApi.getPet() // response looks like PetResult (model above)
.subscribeOn(Schedulers.io())
.subscribe(
{ petList.setSuccess(it) }, // should returns dag, cat, bird, cow
{ petList.setError(it.message) }
)
So, how to merge myPet with otherPet that i get from one API request?

You can achieve the above example like below:
apiClient.getPet.flatMap {
var getListResponse=it;
//From getListResponse you will get Result obj which contain pet and other pet list
var pet=it.getPet()
var petList=it.getPetList()
petList.add(pet)
getListResponse.setPetList=petList
}

Related

Is there any efficient way to search throughout a list of objects every field?

Let's say I have an object
data class Person(
val name: String,
val surname: String,
val street: String,
val postalCode: String,
val telephoneNumber: String,
)
And then I have a list of persons :
val personsList = listOf(
Person(name="John", surname="Hams", street="Gariolg", postalCode="929429", telephoneNumer="+2142422422",),
Person(name="Karl", surname="Hamsteel", street="Gariolg", postalCode="124215", telephoneNumer="+3526522",),
Person(name="Stepf", surname="Hiol", street="Bubmp", postalCode="5342", telephoneNumer="+7574535",),
Person(name="Germa", surname="Foo", street="Hutioa", postalCode="235236", telephoneNumer="+112355",)
)
So now if the user types for instance Hams it should return John and Karl, because both have the word "Hams" inside the object. What I mean is doesn't matter if the user types postalCode, name, or whatever I'd like to loop throughout the object to check if there's any coincidence.
How i would do it, is create a function inside the data class, say, for example, like this. This will check if any field inside your data class matches with the given string.
In my example i check if whole string matches, but you can change this however you want. You probably want it.contains(searchString) inside the any block.
fun checkIfStringMatches(searchString: String) : Boolean =
setOf(this.name, this.surname, this.strees, this.postalCode, this.telephone).any { it == string }
Then, you can use this function on your list of persons to filter if any object matches your string search.
personList.filter{it.checkIfStringMatches(mySearchString)} // this will return a list with all the objects that match your search criteria
The problem is that if you add more fields, you will have to change this function and add it to the listOf() block. But i don't know any way to do this automatically without reflection, which is not really recommended to use. If you still want to use it, here is a question on this topic. Kotlin: Iterate over components of object
Try this, it will work.
personsList.filter { it.surname.startsWith("Hams") }.map {
Log.d("filter_name", it.name)
}
Hey You can apply filter method on list and grab the expected output like below :
val filtered = personsList.filter { it.toString().contains("Hams", true) }

How to covert the $document.data details to useful format so that I could use it in listviews

I would like to convert $document.data details to useful format so that I could use it for further applications. This is data from firestore documents.
private val mFireStore = FirebaseFirestore.getInstance()
mFireStore.collection("Users").whereEqualTo("lastName","H").whereEqualTo("firstName", "Uc").get()
.addOnSuccessListener{ documents ->
for(document in documents){
Log.d("TAG","${document.id}=>${document.data}")
Toast.makeText(applicationContext, "${document.id} => ${document.data}",
Toast.LENGTH_LONG).show()
}
}
.addOnFailureListener{exception ->
Log.w("TAG","Error getting documents:",exception)
Toast.makeText(applicationContext, "Failed",
Toast.LENGTH_LONG).show()
}
This is my code. Now when I run the code get this in the logcat
OL0rD4UfgHSh2K8UoTMnX6Xea6P2=>{lastName=H, image=, firstName=Uc, B=L, gender=, organization=, profileCompleted=0, mobile=0, blood group=o+, id=OL0rD4UfgHSh2K8UoTMnX6Xea6P2, email=jojoy09#gmail.com}
Now I want to convert this result to a useful format so that I could use it later. I wpuld like to convert the data so that I could load it in listview.
In the following for-loop:
for(document in documents) { ... }
The "document" object is of type DocumentSnapshot. When you call getData() on such an object, the type of object that is returned is a Map<String, Object>.
In Kotlin, this object is represented by Map<String, Any>. In order to get the data, you can simply iterate over the Map and get the data accordingly, using the following lines of code:
val map = document.data
for ((key, value) in map) {
Log.d("TAG", "$key = $value")
}
Or even simpler, using:
map.forEach { (key, value) -> Log.d("TAG", "$key = $value") }
However, if you only need, the value of a particular property, for example, the value of the email address, you can simply get it by using DocumentSnapshot#getString(String field) method:
val email = document.getString("email")
Log.d("TAG", email)
The result in the logcat will be:
jojoy09#gmail.com
.................
As I see in your screenshot, almost all of the properties are of type String. However, you can find different flavors for each type of field, like getLong(String field), getDouble(String field), getDate(String field), getTimestamp(String field), and so on.
Furthermore, if you need to get the entire document, and you want to convert it into an object of a specific class, as also #mehulbisht mentioned in his answer, you should use DocumentSnapshot#toObject(Class valueType). So assuming that you have a data class that looks like this:
data class User(
var email: String? = null,
var firstName: String? = null,
var lastName: String? = null,
//Other necessary fields
)
To convert the "document" object into an object of the "User" class, please use the following line of code:
val user = document.toObject(User::class.java)
Log.d("TAG", user.email)
The result in the logcat will be the same as above.
If you want to display a list of "User" objects in a ListView, then please check my answer from the following post:
What miss, with connect Firestore and ListView for random results in sample?
It's really simple to convert the Java code into Kotlin.
The Model that you used to set this data will be used here. You can convert the documents to your Model class using the .toObjects() method on it. Just use:
val myObjs = documents.toObjects(Model::class.java)
EDIT
For displaying this as a Log in Logcat use:
Log.d("myObjs ","""
$myObjs
""".trimIndent())
Do tell if this doesn't work for you :)

Access Room DB inside Transformation.map android

In my Database i have a table called Account which looks kinda like this
#Entity(tableName = "accounts", primaryKeys = ["server_id", "account_id"])
data class Account(
#ColumnInfo(name = "server_id")
val serverId: Long,
#ColumnInfo(name = "account_id")
val accountId: Int,
#ColumnInfo(name = "first_name", defaultValue = "")
var firstname: String
)
So lets say that we have the following Database snapshot
server_id account_id first_name
1 10 Zak
1 11 Tom
1 12 Bob
1 13 Jim
1 14 Mike
Now i also have the following POJO which represents an available video room inside a chatRoom
data class RoomInfo(
#SerializedName("m")
val participantIntList: List<Int>,
#SerializedName("o")
val roomId: String,
#SerializedName("s")
val status: Int
)
So i get an incoming response from my Socket which is like the following
[
{"m": [10, 11, 12], "o": "room_technical", "s": 1},
{"m": [13, 14], "o": "room_operation", "s": 1}
]
which i map it in a List so i have
val roomInfo: LiveData<List<RoomInfo>> = socketManager.roomInfo
// So the value is basically the json converted to a list of RoomInfos using Gson
In order to display this available list of Rooms to the User i need to convert the m (which is the members that are inside the room right now) from accountIds to account.firstnames.
So what i want to have finally is a List of a new object called RoomInfoItem which will hold the list of the rooms with the accountIds converted to firstNames from the Account table of the Database.
data class RoomInfoItem(
val roomInfo: RoomInfo,
val participantNames: List<String>
)
So if we make the transformation we need to have the following result
RoomInfo (
// RoomInfo
{"m": [10, 11, 12], "o": "room_technical", "s": 1},
// Participant names
["Zak", "Tom", "Bob"]
)
RoomInfo (
// RoomInfo
{"m": [13, 14], "o": "room_operation", "s": 1},
// Participant names
["Jim", "Mike"]
)
My Activity needs to observe a LiveData with the RoomInfoItems so what i want is given the LiveData<List> to transform it to LiveData<List>. How can i do that?
Well, finally i could not find a solution but i think that what i am trying to achieve, cannot be done using the Transformation.switchMap or Transformation.map
As I understand you want get LiveData<List<RoomInfoItem>> by analogy LiveData<List<ResultData>> in my sample. And you have next condition: you want to observe list of RoomInfo and for each RoomInfo in this list you want to observe participantNames. (Each pair of RoomInfo and participantNames you map to RoomInfoItem). I think you can achive this behaviour by using MediatorLiveData. I show sample how you can do this bellow:
// For example we have method which returns liveData of List<String> - analogy to your List<RoomInfo>
fun firstLifeData(): LiveData<List<String>> {
//TODO
}
// and we have method which returns liveData of List<Int> - analogy to your participantNames(List<String>)
fun secondLifeData(param: String): LiveData<List<Int>> {
//TODO
}
//and analogy of your RoomInfoItem
data class ResultData(
val param: String,
val additionalData: List<Int>
)
Then I will show my idea of implementation of combined liveDatas:
#MainThread
fun <T> combinedLiveData(liveDatas: List<LiveData<T>>): LiveData<List<T>> {
val mediatorLiveData = MediatorLiveData<List<T>>()
// cache for values which emit each liveData, where key is an index of liveData from input [liveDatas] list
val liveDataIndexToValue: MutableMap<Int, T> = HashMap()
// when [countOfNotEmittedLifeDatas] is 0 then each liveData from [liveDatas] emited value
var countOfNotEmittedLifeDatas = liveDatas.size
liveDatas.forEachIndexed { index, liveData ->
mediatorLiveData.addSource(liveData) { value ->
// when liveData emits first value then mack it by decrementing of countOfNotEmittedLifeDatas
if (!liveDataIndexToValue.containsKey(index)) {
countOfNotEmittedLifeDatas--
}
liveDataIndexToValue[index] = value
// when countOfNotEmittedLifeDatas is 0 then all liveDatas emits at least one value
if (countOfNotEmittedLifeDatas == 0) {
// then we can push list of values next to client-side observer
mediatorLiveData.value = liveDataIndexToValue.toListWithoutSavingOrder()
}
}
}
return mediatorLiveData
}
fun <V> Map<Int, V>.toListWithoutSavingOrder(): List<V> = this.values.toList()
/**
* Key should be an order
*/
fun <V> Map<Int, V>.toListWithSavingOrder(): List<V> = this.entries.sortedBy { it.key }.map { it.value }
/*
or you can run [for] cycle by liveDataIndexToValue in [combinedLiveData] method or apply [mapIndexed] like:
liveDatas.mapIndexed{ index, _ ->
liveDataIndexToValue[index]
}
to receive ordered list.
*/
And how to use all of that together:
fun resultSample(): LiveData<List<ResultData>> {
return firstLifeData().switchMap { listOfParams ->
val liveDatas = listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }
combinedLiveData(liveDatas)
}
}
// u can add extension function like:
fun <T> List<LiveData<T>>.combined(): LiveData<List<T>> = combinedLiveData(this)
// and then use it in this way
fun resultSample_2(): LiveData<List<ResultData>> = firstLifeData().switchMap { listOfParams ->
listOfParams.map { param -> secondLifeData(param).map { ResultData(param, it) } }.combined()
}
I suggest you to consider using room's Relations. I think by room's Relations you can get LiveData<RoomInfoItem> . I cant get you more details about this approach because I don't know details about your data scheme and domain, at the moment.

How do I refer to data class property names for Firestore queries?

I'm using a simple data class Track, then saving it as an object so that fields and values are automatically saved. This is working as intended. However later when I want to query on 1 of those property/field names, I need to provide the String value.
How do I refer to the name of that property in the data class, so that I maintain the "single source of truth" for that value, without hard coding it a second time in the query? Example uses "spotifyId":
data class Track(
val spotifyId: String,
val name: String,
val artist: List<String>,
val duration: String
)
Save a track:
set(trackDocRef, track)
Query for a track:
db.collection("tracks").whereEqualTo("spotifyId", "sdfgsdfswer4543w5yer345").get()
Thank you!
I figured out one way of doing it, not sure how "correct" it is. I used reflection in the TracksContract object to refer to the Track model:
data class Track(
val spotifyId: String,
val name: String,
val artist: List<String>,
val duration: String
)
Single reference to Track model via TracksContract:
object TracksContract {
internal const val COLLECTION_NAME = "Tracks"
object Fields {
val SPOTIFY_ID = Track::spotifyId.name
val NAME = Track::name.name
val ARTIST = Track::artist.name
val DURATION = Track::duration.name
}
}
now if I need to run the query based on that spotifyId, I refer to through the TracksContract:
db.collection(TracksContract.COLLECTION_NAME)
.whereEqualTo(TracksContract.Fields.SPOTIFY_ID, "ID_VALUE")
.get()
Finally if I decide to change the property names in the Track model then the TracksContract will show a compiler error, and I can change the name and refactor references from there if I want to.

Filter last element and add to top of list in mutable list kotlin

I am new in Kotlin. I need your help to sort my mutableList in custom data class. I need to find the search element in the list and put into the top of list. Mostly the search element is in the last element. So I don't know how to filter that. Please give me some suggestions how to achieve that. For example
Data Class
data class Person(val firstName: String, val lastName: String)
data class Item(val gender: Int, val human: List<Human>)
data class Human(val id: Int, val person: List<Person>)
I entered some fake data
val people = mutableListOf(
Item(1,
listOf(
Human(
1,
listOf(
Person("Ragnar", "Lodbrok"),
Person("Bjorn", "Ironside"),
Person("Sweyn", "Forkbeard")
)
),
Human(
2,
listOf(
Person("Ragnar", "Lodbrok"),
Person("Bjorn", "Ironside"),
Person("Sweyn", "Forkbeard")
)
)
)
)
)
If i want to search Forkbeard and want to put in top of list. But i am unable to do this. So please suggest me some good advice.
I tried this but not working
people.forEach { people ->
people.human.forEach { human ->
human.person.sortedByDescending { person ->
person.lastName == "Forkbeard"
}
}
}
I am getting this
[Item(gender=1, human=[Human(id=1, person=[Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside), Person(firstName=Sweyn, lastName=Forkbeard)]), Human(id=2, person=[Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside), Person(firstName=Sweyn, lastName=Forkbeard)])])]
Answer
I want this
[Item(gender=1, human=[Human(id=1, person=[Person(firstName=Sweyn, lastName=Forkbeard),Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside)]), Human(id=2, person=[Person(firstName=Sweyn, lastName=Forkbeard),Person(firstName=Ragnar, lastName=Lodbrok), Person(firstName=Bjorn, lastName=Ironside)])])]
[ Sweyn Forkbeard, Ragnar Lodbrok, Bjorn Ironside ]
Thanks a lot
A trick to do all of this in just one line in Kotlin is with sortBy
people.sortByDescending { it.lastName == "Forkbeard" }
To get a single item from a list, you can use the first function:
val sweyn: Person = people.first { it.lastName == "Forkbeard" }
Then, for adding sweyn at the top:
people.remove(sweyn)
people.add(0, sweyn)
If you need to constantly do this, maybe you are looking for a Queue
EDIT:
For the nested human list, you can do the same if you change the person's type to MutableList in the Human data class:
people.forEach { people ->
people.human.forEach { human ->
val sweyn: Person = human.person.first { it.lastName == "Forkbeard" }
human.person.remove(sweyn)
human.person.add(0, sweyn)
}
}

Categories

Resources