What is an idiomatic way to map keys to a matching list of lists? An example - given:
val s = listOf(1, 9)
val u = listOf(listOf(1, 2, 3), listOf(1, 4, 7), listOf(1, 5, 9))
I would like to have a Map<Int, List<List<Int>>> such that every key in s is mapped to a list of lists containing that key:
{1=[ [1, 2, 3], [1, 4, 7], [1, 5, 9] ], 9=[ [1, 5, 9] ]}
The following:
s.groupBy({ it }, { x -> u.filter { it.contains(x) } })
produces:
{1=[[[1, 2, 3], [1, 4, 7], [1, 5, 9]]], 9=[[[1, 5, 9]]]}
which is not quite right and it isn't clear how to flatten the result to the expected shape.
I would recommend associateWith and use it like this:
s.associateWith { num -> u.filter { list -> num in list } }
Output:
{1=[[1, 2, 3], [1, 4, 7], [1, 5, 9]], 9=[[1, 5, 9]]}
I recommended associate at first, but you can shorten the code even further if you use associateWith. Thanks to Abhay Agarwal who recommended it.
Update
You just need to flatten the values of the result Map.
val w = s.groupBy({ it }, { x -> u.filter { it.contains(x) } })
.mapValues { it.value.flatten() }
My solution map the first collection to pairs from each element to the list where it appears, and then groupBy the result list.
Example
val w = s.map { elem -> Pair(elem, u.filter { list -> elem in list }) }
.groupBy ({ it.first }, { it.second })
.mapValues { it.value.flatten() }
check(w[1] == listOf(listOf(1, 2, 3), listOf(1, 4, 7), listOf(1, 5, 9)))
check(w[9] == listOf(listOf(1, 5, 9)))
println(w)
Output
{1=[[1, 2, 3], [1, 4, 7], [1, 5, 9]], 9=[[1, 5, 9]]}
Idiomatic to me would be s.groupBy(....) The answer by #Omar Mainegra - s.groupBy(...).mapValues( flatten ) absolutely works but it looks like a hack where the initial result needs some extra massaging.
The issue is with the implementation of groupBy and more specifically with groupByTo:
public inline fun <T, K, V, M : MutableMap<in K, MutableList<V>>> Iterable<T>.groupByTo(destination: M, keySelector: (T) -> K, valueTransform: (T) -> V): M {
for (element in this) {
val key = keySelector(element)
val list = destination.getOrPut(key) { ArrayList<V>() }
list.add(valueTransform(element))
}
return destination
}
The implementation wraps the values associated with a key in a list because in general multiple values can be associated with a key which is not the case here
where values in s are unique which means that groupBy is the wrong function to use. The right function is associateWith:
s.associateWith { x -> u.filter { it.contains(x) } }
produces:
{1=[[1, 2, 3], [1, 4, 7], [1, 5, 9]], 9=[[1, 5, 9]]}
Related
I want to create the following one with a fancy notation:
arrayOf(intArrayOf(4, 5), intArrayOf(5, 8), intArrayOf(1, 9), intArrayOf(8, 10), intArrayOf(1, 6))
at least, cannot I achieve something that looks as follows:
arrayOf<IntArray>((4, 5), (5, 8), (1, 9), (8, 10), (1, 6))
because it is pretty awkward to rewrite intArrayOf for each row to put in.
Note that I do not ask for the following syntax I'm aware of which is used to initialize an empty matrix with values that are either same or following a common pattern.
val array = Array(row) { IntArray(column) }
val result = listOf(4, 5, 5, 8, 1, 9, 8, 10, 1, 6)
.chunked(2)
.map { it.toIntArray() }
.toTypedArray()
Edit 1:
The calculation could go into an extension function:
fun List<Int>.toArrayOfIntArrays() = this.chunked(2).map { it.toIntArray() }.toTypedArray()
val result = listOf(4, 5, 5, 8, 1, 9, 8, 10, 1, 6).toArrayOfIntArrays()
Edit 2:
Another option – assuming that the inner IntArrays all will consist of exactly two elements – would be to use a user-defined infix function:
infix fun Int.with(i1: Int) = intArrayOf(this, i1)
val result = arrayOf(4 with 5, 5 with 8, 1 with 9, 8 with 10, 1 with 6)
Any word not in conflict with the Kotlin keywords could be used, 'with' is just an example.
If you simply don't like the wordiness of using intArrayOf you could define a shorter name to do the same, for example
fun i(vararg e: Int) = intArrayOf(*e)
And then do
arrayOf(i(4, 5), i(5, 8), i(1, 9), i(8, 10), i(1, 6))
Here is my solution, abusing operator overloading ;)
object Matrix {
operator fun get(vararg rows: IntArray) = rows
operator fun get(vararg values: Int): IntArray = intArrayOf(*values)
}
fun main() {
val result = Matrix.let {
it[
it[1, 2, 3],
it[4, 5, 6],
it[7, 8, 9],
]
}
println(result.toList().map { it.toList() }) // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
}
Please don't use it
This might be something really simple, but I just can't figure it out. What I'm trying to do is take 2 arrays and filter out what I don't need and only
return the one array.
So what I have right now is this
let array1 = [1, 2, 3];
let array2 = [1, 2, 3, 4, 5, 6];
and what I would like is to return array 2 with only the items that doesn't show up in array1 so that would be 4, 5,6.
This is what I have so far
return array1.forEach(a => {
array2.filter(aa => aa !== a)
});
and that doesn't return anything
let array1 = [1, 2, 3];
let array2 = [1, 2, 3, 4, 5, 6];
let array3 = array2.filter(i => !array1.includes(i));
console.log(array3)
This might help to solve your problem.
let array1 = [1, 2, 3]
let array2 = [1, 2, 3, 4, 5, 6]
function returnList(arOne,arTwo){
return arTwo.filter(a => !arOne.includes(a))
}
let response = returnList(array1 ,array2 );
I need help. I trying make array only with odd numbers but I don't want use arraylist because I only want array.
Input array like this: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I am trying to get odd only array like : [1, 3, 5, 7, 9]
val array = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val arraylist = arrayListOf<Int>()
for(i in 0..array.size - 1) {
if(array[i] % 2 != 0)
arraylist.add(array[i])
}
val oddarray = arraylist.toArray()
Why not just use filter:
import java.util.Arrays;
fun main(args: Array<String>) {
val numbersArray = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val oddArray = numbersArray.filter{ it % 2 != 0 }.toTypedArray()
print(Arrays.toString(oddArray)) // [1, 3, 5, 7, 9]
}
Considering this code, using Ramda 0.21.0:
var iteratee = (acc, [k, v]) => {
acc[k] = ++v;
return acc
}
var foo = R.pipe(
R.toPairs,
R.reduce(iteratee, {})
)
console.log(foo({ a: 1, b: 2})) // { a: 2, b: 3 }
console.log(foo({ c: 3, d: 4})) // { a: 2, b: 3, c: 4, d: 5 }
Why does the second call to foo display { a: 2, b: 3, c: 4, d: 5 } instead of { c: 4, d: 5 }?
Is there some kind of memoization going on? I would expect the initial value of acc to be reset to {} each time foo is applied.
This answer mostly expands on the comments by #iofjuupasli
The problem is the mutation of the accumulator object. You create one in the definition of foo which is reused on every call, and then you update it in iteratee (horrible name, IMHO. Call it bar or something. :-) ). There are several ways you could fix this. One might be to make sure that you pass a new accumulator on each call to foo:
var iteratee = (acc, [k, v]) => {
acc[k] = ++v;
return acc
}
var foo = R.pipe(
R.toPairs,
list => R.reduce(iteratee, {}, list)
)
foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}
This works, but feels unsatisfying. Perhaps more helpful would be to avoid mutating the accumulator object on each pass. assoc will create a new object that reuses as much of the previous one as possible:
var iteratee = (acc, [k, v]) => R.assoc(k, v + 1, acc)
var foo = R.pipe(
R.toPairs,
R.reduce(iteratee, {})
);
foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}
This seems cleaner. But in fact Ramda has a much simpler solution. The map function treats objects as functors to be mapped over. Combining this with inc, which simply increments a value, we can just do this:
var foo = R.map(R.inc);
foo({ a: 1, b: 2}); //=> {"a": 2, "b": 3}
foo({ c: 3, d: 4}); //=> {"c": 4, "d": 5}
And that feels really clean!
I started learning C some weeks ago and today I started learning Swift. The code is the following:
import Foundation
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 8, 16, 25],
]
var largest = 0;
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number;
}
}
}
println(largest);
Why do I need kind in the for-in thingy? For "Prime", "Square", ..., right? Can I work with that somehow, too?
“Add another variable to keep track of which kind of number was the largest, as well as what that largest number was.”
How do I build that in?
import Foundation
var largest = 0;
var largestKind: String?;
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 8, 16, 25],
]
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number;
largestKind = kind;
}
}
}
println("The number \(largest) is from the type \(largestKind)");
That's my solution at the moment. However, the output is
The number 25 is from the type Optional("Square")
How do I get rid of the 'Optional("")? I just want the word Square. I tried removing the question mark (var largestKind: String?; to var largestKind: String;) but I get an error doing that.
For those who have the same question, this is another solution I've found. var largestKind is still optional because of String? but the exclamation mark at the end \(largestKind!) makes it possible to access the value without having that optional stuff around the actual content.
import Foundation
var largest = 0;
var largestKind: String?;
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 8, 16, 25],
]
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number;
largestKind = kind;
}
}
}
println("The number \(largest) is from the type \(largestKind!).");