Transform an array of value with async function and Reactive-Cocoa - objective-c

I have an array of String and I want to modify it by calling an asynchronous function on each value.
I tried something but it's just creating a RACSequence of RACSignal and not an array of modified String
func modifyValueAsynchronously(inputValue: String, completion: (String -> Void)) -> RACSignal {
return RACSignal.createSignal() { subscriber in
doAServerCall(inputValue) { outputValue in
subscriber.sendNext(outputValue)
}
}
}
let value: NSArray = ["a", "b"]
let result = value.rac_sequence.map({ value in
return self.doRequest(value as! String)
})

As you said, you want to perform an asynchronous function on each of your string values in array. Because of the fact that you do it async, the final result will also arrive asynchronous. After getting RACSequence of RACSignal, you want to wait until each signal provides you with value and then gather it all into array.
let transformation = { (input: String) -> RACSignal in
return RACSignal.createSignal { subscriber in
subscriber.sendNext(input + "a")
return nil
}
}
let array: NSArray = ["a", "b", "c", "d"]
let sequence = array.rac_sequence.map({ (element) in
return transformation(element as! String)
})
RACSignal.combineLatest(sequence).subscribeNext { (element) in
let array = (element as! RACTuple).allObjects()
print("Elements from array \(array) \n")
}
Lets take this example. Transformation closure will asynchronously add an 'a' letter to each string. After creating a sequence, I add combineLatest operator which will give me result as soon as each element from sequence provides me with it's result. As soon as I get all results, I get a RACTuple inside subscribeNext method and I can make an array out of it.

Related

Kotlin sort one List with key and Enum with key and order

I receive data from Request information as list data (List) below code. That data has a "key" parameter by which I want to sort it.
data class ApplianceSetting(
#SerializedName("key") val key: String,
#SerializedName("value") var value: Any,
(...)
I have the required order in the SettingsUtilEnum and want to sort items by that.
After that, I can convert the list using map{} the data and use the function of Enum getSettingByMode() and get the list of Enum values. Then I will sort them and convert them again to List.
But that sounds too inefficient. Is there a better way.
enum class SettingsUtilEnum(
var settingKey: String,
override val order: Int = 99,
var settingName: String = "",
) : AbstractOrderEnum {
FIRST_MODE("first.mode", 0),
SECOND_MODE("second.mode", 1),
(...)
UNKNOWN_MODE("", 99);
companion object {
#JvmStatic
fun getSettingByMode(settingKey: String): SettingsUtilEnum? {
return values().find { it.settingKey == settingKey }
}
k
private fun initDataObserver() {
(activity as FavouriteActivity).viewModel.applianceSettings.observe(activity as FavouriteActivity
) { data ->
(controlRecyclerView.adapter as FavouriteAdditionalControlsAdapter)
val adapter = (controlRecyclerView.adapter as FavouriteAdditionalControlsAdapter)
// public final var data: List<ApplianceSetting>
// old code:
// data.settings
adapter.data = sortAndGetControlModes(data)
adapter.notifyDataSetChanged()
}
}
// TODO: sortAndGetControlModes
private fun sortAndGetControlModes(data: ApplianceSettingsList) =
data.settings.map {
getSettingByMode(it.key)
?: UNKNOWN_MODE.apply {
// If in future new modes are added -> put them as tail
settingKey = it.key
}
}.sortedBy { it.order }
// error i need to return again List<ApplianceSetting>
If you want to compare keys with theirs ASCII values you can just use sortBy { it.key }
If you want to expand possibilities of comparison you can use function sortedWith with passing custom comparator as argument.
Comparator used to compare its two arguments for order. Returns zero if the arguments are equal, a negative number if the first argument is less than the second, or a positive number if the first argument is greater than the second.
Example:
You can use it like that if you want to sort by integer value of key parameter:
data.settings.sortedWith { a, b ->
when {
a.key.toInt() < b.key.toInt() -> -1
a.key.toInt() > b.key.toInt() -> 1
else -> 0
}
}
I fixed it using sortedBy and as comparator I am using received value (order) from getSettingByMode(), if item is not found (null) I give him order value of 99 and put it on tail position:
private fun sortAndGetControlModes(data: ApplianceSettingsList) =
data.settings.sortedBy {
getSettingByMode(it.key)?.order ?:99
}

Kotlin recursive function to compare two array and keep the content of origin array

I need to compare two MutableList, requiredDevices and providedDevices using recursive function. These two MutableList is the element of a MutableList, originalArray as well. If there is a match, it will take out the matched element of requiredDevices and providedDevices, one by one, and add the leftover to list1 and list2, respectively. How can I do this and keep the content of requiredDevices and providedDevices and originalArray . For example:
originalArray=[requiredDevices, providedDevices]
=[["A","B","C"],["B,"C","A","A","D"]]
requiredDevices=["A","B","C"]
providedDevices=["B,"C","A","A","D"]
, after the recursion:
list1=[] (empty array)
list2=["A","D"]
originalArray=[[],["A","D"]]
Tried to write a recursive function and take requiredDevices and providedDevices as argument. The recursive function returns a list temp contains list1 and list2. But requiredDevices and providedDevices is modified as well.
val temp = mutableListOf<MutableList<String>>()
fun compareArray(requiredDevices: MutableList<String>, providedDevices: MutableList<String>): List<MutableList<String>> {
for (i in 1 until requiredDevices.size) {
for (j in 0 until providedDevices.size) {
try {
if (requiredDevices[i] == providedDevices[j]) {
requiredDevices.removeAt(i)
providedDevices.removeAt(j)
compareArray(requiredDevices, providedDevices)
temp.add(requiredDevices)
temp.add(providedDevices)
}
} catch (e: IndexOutOfBoundsException) {
}
}
}
return temp.distinct()
}
I want to keep the originalArray content and still get the same result of list1 and list2. As following:
originalArray=[["A","B","C"],["B,"C","A","D"]]
I don't really follow what your algorithm is trying to do. Based on your comment, I think this solves the problem. This copies the two starting lists into new lists, and then removes items from the copies one-by-one, so they will retain excess items if they have them.
fun <T> removeCommonValues(listA: List<T>, listB: List<T>): Pair<List<T>, List<T>> {
val outA = listA.toMutableList()
val outB = listB.toMutableList()
for (i in (outA.size - 1) downTo 0){
for (j in (outB.size - 1) downTo 0){
if (outA[i] == outB[j]){
outA.removeAt(i)
outB.removeAt(j)
break
}
}
}
return outA to outB
}
Test:
fun main() {
val required = listOf("A", "B", "C", "C")
val available = listOf("B", "C", "A", "D", "D")
println(removeCommonValues(required, available))
}
// Outputs ([C], [D, D])

Removing elements from a list which are not in another list - Kotlin

I have two mutableLists, listOfA has so many objects including duplicates while listOfB has fewer. So I want to use listOfB to filter similar objects in listOfA so all list will have equal number of objects with equivalent keys at the end. Code below could explain more.
fun main() {
test()
}
data class ObjA(val key: String, val value: String)
data class ObjB(val key: String, val value: String, val ref: Int)
fun test() {
val listOfA = mutableListOf(
ObjA("one", ""),
ObjA("one", "o"),
ObjA("one", "on"),
ObjA("one", "one"),
ObjA("two", ""),
ObjA("two", "2"),
ObjA("two", "two"),
ObjA("three", "3"),
ObjA("four", "4"),
ObjA("five", "five")
)
//Use this list's object keys to get object with similar keys in above array.
val listOfB = mutableListOf(
ObjB("one", "i", 2),
ObjB("two", "ii", 5)
)
val distinctListOfA = listOfA.distinctBy { it.key } //Remove duplicates in listOfA
/*
val desiredList = doSomething to compare keys in distinctListOfA and listOfB
for (o in desiredList) {
println("key: ${o.key}, value: ${o.value}")
}
*/
/* I was hoping to get this kind of output with duplicates removed and comparison made.
key: one, value: one
key: two, value: two
*/
}
If you want to operate directly on that distinctListOfA you may want to use removeAll to remove all the matching entries from it. Just be sure that you initialize the keys of B only once so that it doesn't get evaluated every time the predicate is applied:
val keysOfB = listOfB.map { it.key } // or listOfB.map { it.key }.also { keysOfB ->
distinctListOfA.removeAll {
it.key !in keysOfB
}
//} // if "also" was used you need it
If you have a MutableMap<String, ObjA> in place after evaluating your unique values (and I think it may make more sense to operate on a Map here), the following might be what you are after:
val map : MutableMap<String, ObjA> = ...
map.keys.retainAll(listOfB.map { it.key })
retainAll just keeps those values that are matching the given collection entries and after applying it the map now contains only the keys one and two.
In case you want to keep your previous lists/maps and rather want a new list/map instead, you may just call something like the following before operating on it:
val newList = distinctListOfA.toList() // creates a new list with the same entries
val newMap = yourPreviousMap.toMutableMap() // create a new map with the same entries
I tried this
primaryList.removeAll { primaryItem ->
secondaryList.any { it.equals(primary.id, true) }
}
PrimaryList here is a list of objects
SecondaryList here is a list of strings

Mapping an iterable 1-to-n in kotlin

What I am trying to do is use the Iterable.map but instead of transforming every one value to one new value, I want to transform one value to multiple new values.
For example:
val myList = listOf("test", "123", "another.test", "test2")
val result = myList.map {
if(it.contains(".")) {
return#map it.split(".")
} else {
return#map it
}
}
//desired output: ["test", "123", "another", "test", "test2"]
This code would result in a List which contains both strings and lists of strings (type Any).
How could I most elegantly implement this?
One quick way to do this is using flatMap.
val output = myList.flatMap { if(it.contains(".")) it.split(".") else listOf(it) }
The flatMap method transforms the each element using given function and then flatten the result to a single list.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/flat-map.html

Type inference with functional builders

I'm using Kotlin KBuilders with some protobuffs and have run into a situation that is confusing me.
To start off, I have a function that takes a file name and list of serialized JSON and deserialized that JSON to a protobuff.
fun parseFileData(fileName: String, lines: List<String>): Data.Builder.() -> Unit = when (fileName) {
SOME_FILE_NAME -> deserializeLinesToModel(lines, DataModel::class.java)
.let {
return {
dataMeasurement = buildDataMeasurement {
property1 = it.reduce { acc, n -> acc + n }
measurementMsec = it.map { it.measurementMsec }
}
}
}
else -> throw UnsupportedOperationException()
The first thing I didn't understand was why I needed the return inside the let block. But it worked so I moved on.
I later decided to refactor some stuff to make code elsewhere simpler and ended up with something like this:
fun parseFileData(fileName: String, factory: DataFactory): Sequence<Data.Builder.() -> Unit> = when (fileName) {
SOME_FILE_NAME -> factory.getSomeFileSequence() // returns Sequence<Model>
.batch(1000) // process data in batches of 1000 to reduce memory footprint and payload size
.map { return {
dataMeasurement = buildDataMeasurement {
property1 = it.reduce { acc, n -> acc + n }
measurementMsec = it.map { it.measurementMsec }
}
}
else -> throw UnsupportedOperationException()
So basically, instead of processes each batch as a list, I read the sequence from the factory, batch it into a sequence of lists and try to map each list to a Data.Builder.() -> Unit. However, this time I get return is not allowed here. I've tried multiple variations, with and without return and map and let and whatnot. The closest I've gotten to is a return type of Sequence<() -> Unit> which fails type inference.
Can anyone explain what's going on here? And why this type cannot be inferred?
return in the map lambda is a non-local return. It tries to return from the closest fun function, which happens to be parseFileData.
Non-local returns are only allowed from inline functions, whose lambda parameters are inlined at the call site, and the map extension for Sequence is not an inline function.
If you want to return a value from the lambda itself, use qualified return return#map ..., or omit it completely: then the last expression in the block will be returned as the result.