I try to make a method that would get a list of movies from database using coroutine and would return me that list.
But as you know coroutine returns Deferred, not a list, so I have a problem here.
suspend fun getMovieList(): List<MovieLocalModel>{
val list = viewModelScope.async {
dbRepository.getMovie().toList()
}
return list
}
How can I convert Deferred List<MovieLocalModel to List<MovieLocalModel?
Or is there a method to get a list from LiveData?
To get an object from Deffered you can use await method:
suspend fun getMovieList(): List<MovieLocalModel>{
val list = viewModelScope.async {
dbRepository.getMovie().toList()
}
return list.await()
}
If you use LiveData you can get an object using value property:
val list = livaDataObj.value
If you don't have a reason this fetch needs to specifically be done in the viewModelScope, which is likely the case since it is only fetching something (not saving something), you can simplify this by calling the function directly.
suspend fun getMovieList(): List<MovieLocalModel> =
dbRepository.getMovie().toList()
Related
I'm practicing leetcode problems to perfect my kotlin syntax and am wondering why this code doesn't work. My question specifically is why doesn't my courses hashmap populate with this code.
Prerequisites is an array in this form [[0,1][0,3][4,5][6,7]] and if I print my variables for pre and post they print what I expect
But I'm trying to turn courses into an adjacency matrix like this {0: [1,3], 4: [5], 6: [7]}
and instead it just prints an empty set every time
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre]?.add(post)
}
print(courses)
return false
}
}
stdout: {}
[] does not give you the default value
From the docs of withDefault:
This implicit default value is used when the original map doesn't contain a value for the key specified and a value is obtained with Map.getValue function
If you want to get the default value, you need to use getValue instead of the index operator.
Using the index operator, you would just get null and because of the the null-safe operator, the add operation would not even be executed.
If you take a look at the relevant source code, you can see that the funxtionality get is not changed when using .withDefault but only getOrImplicitDefault returns the default value.
Getting the default does not set anything
Furthermore, when accessing courses.getValue(pre) in the loop, the Map will be empty. Because of the withDefault, it will return a MutableList where you can add elements but getting such a list and adding elements to it will not add the list to the Map. Reading and accessing an element does not insert it.
Simple solution
If you want to make sure the element is present in the Map, you can use courses[pre]=course.getValue(pre) before reading courses[pre]?:
class Solution {
fun canFinish(numCourses: Int, prerequisites: Array<IntArray>): Boolean {
val courses = HashMap<Int, MutableList<Int>>().withDefault{ mutableListOf<Int>() }
for ((pre, post) in prerequisites){
courses[pre] = courses.getValue(pre)
courses[pre]?.add(post)
}
print(courses)
return false
}
}
If the entry is set already, it will be set to itself (no change) and if it isn't set, it will be set to the default value (empty list).
dan1st's answer covers it - your default list is just returned, not put and returned, so it's not part of the map - but here's a different take to get that functionality:
val courses = HashMap<Int, MutableList<Int>>().run {
withDefault{ key ->
mutableListOf<Int>().also { put(key, it) }
}
}
So basically using the withDefault wrapper, using run so the map is this in the default value function, so you can add your list to the map before returning it. Then when you call courses.getValue(69) you'll get back a list that's already been inserted into the map
If you like, there's also a function that'll do this grouping for you, groupBy
val nums = arrayOf(
intArrayOf(0,1),
intArrayOf(0,3),
intArrayOf(4,5),
intArrayOf(6,7)
)
val groups = nums.groupBy(keySelector = { it[0] }, valueTransform = { it[1] })
println(groups)
>> {0=[1, 3], 4=[5], 6=[7]}
I tried mocking this function but the record variable is always null
Here is the code in the service:
val record: LockDatabaseRecord = lockDatabaseRecordService.getTransLockDbRecord(dbRecordId) {
it.lockDatabaseRecordKeys.size
}
And this is how I mocked it
#Test
fun failedEngageSessionItem() {
val dbVersion = "0x0000001"
val sessionId = UUID.randomUUID()
val dbRecordId = UUID.randomUUID()
val requestDetails = "request details"
val recordDbVersion = "1x"
val lockId = UUID.randomUUID()
val lock = mock(Lock::class.java)
val lockDatabaseRecord = mock(LockDatabaseRecord::class.java)
val lockDatabaseRecordKey = mock(LockDatabaseRecordKey::class.java)
val key = mock(Key::class.java)
`when`(lockService.getTransLock(eq(lockId), any())).thenReturn(lock)
`when`(lockDatabaseRecordService.getTransLockDbRecord(eq(dbRecordId), any())).thenReturn(lockDatabaseRecord)
From the way you call it, it seems like getTransLockDbRecord accepts two arguments (an id and a lambda that returns the size of keys). Make sure you mock it exactly like it is supposed to be called, icluding the lambda parameter.
In the bitecode, these two calls would be calls to two different methods (with overloaded signatures), you need to make sure the correct one is called.
Mockito might also have issues comparing lambdas. If mocking doesn't work I would suggest you first try it with Mockito.any() just to make sure it manages to mock it.
`when`(lockDatabaseRecordService.getTransLockDbRecord(eq(dbRecordId), any()))
.thenReturn(lockDatabaseRecord)
I want to have an immutable list, since I don't really need the mutability so it likely to just cause bugs. However, the list is a lateinit var declared at the class level.
I want to initially populate the list with values from a loop somewhat like this:
for (items in someOtherCollection) {
val itemToAdd = doSomeProcessingOnThisData()
list.add(itemToAdd)
}
However, since the list is immutable, I can't call add(). Is there a better way to init a list such as this without simply adding all the values to a second, mutable list and then assigning it to an immutable list?
My current solution is this, but it just seems inefficient:
val tmpList = mutableListOf<Data>()
foos.forEach() {
val itemToAdd = doSomeProcessing()
foos.add(itemToAdd)
}
this.list = tmpList
If you want to make a new list processing some data in another collection, try this:
this.list = someOtherCollection.map {
doSomeProcessing()
}
Give this a read for a better understanding: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html
I am writing some unit test cases for my application. I want to mock MongoClient update method, but the update returns Single<Void>.
when(mongoClient.rxUpdate(anyString(), any(JsonObject.class), any(JsonObject.class)))
.thenReturn(Single.just(Void))
Now Single.just(Void) doesn't work, what is the correct way of doing it?
--UPDATE--
So I am writing unit test for updateUserProfile method and for that I have mocked service. But the service.updateAccount method return is something I am not able to mock.
//Controller class
public void updateUserProfile(RoutingContext routingContext){
// some code
service.updateAccount(query, update)
.subscribe(r -> routingContext.response().end());
}
//Service Class
public Single<Void> updateAccount(JsonObject query, JsonObject update){
return mongoClient.rxUpdate("accounts", query, update);
}
Because the return type of mongoClient.rxUpdate is Single<Void>, I am not able to mock that part.
For now the workaround which I have figured out is:
public Single<Boolean> updateAccount(JsonObject query, JsonObject update){
return mongoClient.rxUpdate("accounts", query, update).map(_void -> true);
}
But this is just a hacky way of doing it, I want to know how can I exactly create Single<Void>
Having a method returning Single<Void> may raise some concerns, as some users have already expressed their view on this in the comments.
But if you are stuck with this and you really need to mock it (for whatever reason), there are definitely ways to create a Single<Void> instance, for example you could use the create method of the Single class:
Single<Void> singleVoid = Single.create(singleSubscriber -> {});
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleVoid);
Single<Void> result = test.updateAccount(null, null);
result.subscribe(
aVoid -> System.out.println("incoming!") // This won't be executed.
);
Please note: you won't be able to actually emmit a Single item, since Void can't be instantiated without reflection.
A trick that could eventually work in some cases is to ommit the generic type argument and emmit an Object instead, but this could lead easily to a ClassCastException. I would not recommend to use this:
Single singleObject = Single.just(new Object());
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleObject);
Single<Void> result = test.updateAccount(null, null);
// This is going to throw an exception:
// "java.base/java.lang.Object cannot be cast to java.base/java.lang.Void"
result.subscribe(
aVoid -> System.out.println("incoming:" + aVoid)
);
And of course you could use reflection as well (as already suggested by Minato Namikaze):
Constructor<Void> constructor = Void.class.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
Void instance = constructor.newInstance();
Single<Void> singleVoidMock = Single.just(instance);
when(test.updateAccount(any(JsonObject.class), any(JsonObject.class))).thenReturn(singleVoidMock);
Single<Void> result = test.updateAccount(null, null);
result.subscribe(
aVoid -> System.out.println("incoming:" + aVoid) // Prints: "incoming:java.lang.Void#4fb3ee4e"
);
I wanted to have a Queue class that could be used the same way as a list.
For instance,
val q = Queue()
would instantiate an empty queue.
For that purpose I tried using a companion class :
object Queue {
def apply() = new Queue[Any]
}
Is that the right way to do it ?
Using the apply method of the companion object is the right way to do it, but you could also add a type parameter on apply itself:
object Queue {
def apply[T]() = new Queue[T]
}
So that you can create a Queue of the right type:
val q = Queue[Int]()
Usually you also allow populating the sequence on creation, so that the element type can be inferred, as in:
def apply[T](elms: T*) = ???
So that you can do:
val q = Queue(1,2,3) // q is a Queue[Int]
Yes.
If you want to initialise an object without using new, then using apply() as a factory method in the companion is absolutely the right way to go about it.
You might also want to consider a more specific factory (or factories) to help make your code more self-documenting.
object Queue {
def apply[T](xs: T*) = new Queue(xs: _*)
def empty[T] = new Queue[T]()
}