More idiomatic way to initialize this kotlin list? - list

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

Related

Getting a List from Coroutines

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()

Multiple lists share same reference

Ive got a function named allAppointmentList to get Appointments from the server using a GET method in AppointmentProvider class.
In my MyAppointments class I have initialized 2 lists named as appointment and allAppointments as below,
class _MyAppointmentState extends State<MyAppointment> {
bool isLoading = true;
List<Appointment> allAppointments=[];
List<Appointment> appointments = [];
And in the init state I have assigned the data I get from the allAppointmentList method to the 2 lists mentioned above.
#override
void initState() {
super.initState();
_loadAppointments();
}
_loadAppointments() async {
final AppointmentProvider appointmentProvider =
Provider.of<AppointmentProvider>(context, listen: false);
await appointmentProvider.getAllAppointments();
setState(() {
isLoading = false;
appointments = appointmentProvider.allAppointmentList;
allAppointments = appointmentProvider.allAppointmentList;
});
}
when I change one list the other changes as well.For example,
if I clear the appointments list,allAppoitments list gets cleared as well.If I remove the element in the second index of the appointments list,the element in the second index of the allAppointments list gets removed as well.
How can I make these two list act independently ?
Your problem is probably that you think this creates new lists:
appointments = appointmentProvider.allAppointmentList;
allAppointments = appointmentProvider.allAppointmentList;
My guess is that appointmentProvider.allAppointmentList returns the same List instance every time which is a problem here since you are then just assigning the same List object to both appointments and allAppointments.
I am not sure if you also want copies of all the objects inside the lists but if you just want to have independent lists which then contains references to the same objects, the safest would just be to do:
appointments = appointmentProvider.allAppointmentList.toList();
allAppointments = appointmentProvider.allAppointmentList.toList();
This will create new lists which then contains the same objects from appointmentProvider.allAppointmentList. But you can then delete/add elements to each of these lists without this change also happening to the other lists.
appointments and allAppointments are currently just references to appointmentProvider.allAppointmentList. You can create new instances as follows:
appointments = [...appointmentProvider.allAppointmentList];
allAppointments = [...appointmentProvider.allAppointmentList];
This uses a list literal [] in combination with the spread operator ... to create a new list with the same elements. Note that the elements themselves are still references to their original instances and are not deep copies.

Adding to list from hashmap key kotlin

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]}

Flutter: List deprecated? [duplicate]

Why List() constructor is not accessible after Dart's null safety?
// Compile time error: 'List' is deprecated and shouldn't be used.
// The default 'List' constructor isn't available when null safety is enabled.
// Try using a list literal, 'List.filled' or 'List.generate'.
List<int> foo = List();
However, you can still do:
List<int> foo = []; // No error
So, what's the difference between the two? Either both of them should show the error or none of them.
Short answer:
Instead of the pre-null-safety operations
var foo = List<int>(); // Now error
var bar = List<int>(n); // Now error
var baz = List<int>(0); // Now error
use the following:
var foo = <int>[]; // Always the recommended way.
var bar = List.filled(1, 0); // Not filled with `null`s.
var baz = List<int>.empty();
Long answer:
The List constructor had two uses:
new List() to create an empty growable list, equivalent to [].
new List(n) to create a fixed-length list of length n filled with null values
With null safety, the second use was unsound most of the time, and there was no good way to fix it. It's possible to force a type argument to be non-nullable, but List<T>(4) only works when T is nullable. There is no way to enforce that.
So, the List(n) mode needed to go (replaced by List.filled(n, value) which forces you to provide a fill-value).
That left List(), which doesn't really carry its own weight. You can just use [] instead (and you should!), so it was decided to remove the constructor entirely - all uses of it was either unsafe or useless.
(Also, it was a weird constructor already, because if we wanted to properly make it null safe, it would have an optional parameter with a non-nullable type and no default value.)
By removing it completely, it makes it possible to, potentially, introduce a new List constructor in the future, perhaps as a shorter alias for List.filled. One can hope.
Apart from what #lrn sir mentioned, you can also create a list using:
List<int> foo = List<int>.empty(growable: true); // []
This is because the the default List() element was deprecated how about you try using List.filled() element as shown below
void display() {
var fixedList = new List<int>.filled(5, 0, growable: false);
fixedList[0] = 0;
fixedList[1] = 10;
fixedList[2] = 20;
fixedList[3] = 30;
fixedList[4] = 40;
print('Elements in the list are as follows: $fixedList');
}
}
While for the Growable Length List you can try doing as shown below:
void main() {
var growableList = new List<int>.filled(0,0, growable:true);
growableList = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90];
print('The elements in the growable list include: $growableList');
}
_items = List<DropdownMenuItem<String>>(); can be written in null safty as
_items = List<DropdownMenuItem<String>>.from(<DropdownMenuItem<String>>[]);

Kotlin List and MutableList: two different references, pointed to the same collection object

Could you give an example to this quotation:
A key thing to keep in mind when working with collection interfaces is that read-only collections aren’t necessarily immutable. If you’re working with a variable that has a read-only interface type, this can be just one of the many references to the same collection. Other references can have a mutable interface type
I want to write a function that adds elements to some collection, while creating a val someCollection: List <> in the fun main(). I can do this through the var someCollection: List <> = funAdd(someCollection.toMutable), but can I do it like this without using a variable var?
Example
fun addEl(numbers:MutableList<Int>):List<Int>{
for (i in 1..10){
numbers.add(i)
}
return numbers.toList()
}
fun main(args: Array<String>){
var readOnlyNumbers: List<Int> = emptyList()
readOnlyNumbers = addEl(readOnlyNumbers.toMutableList())
println(readOnlyNumbers.size)
}
Can I avoid using var and reassigment readOnlyNumbers or not?
Here are various ways of rewriting your code without using var:
fun addEl(numbers:MutableList<Int>):List<Int>{
for (i in 1..10) {
numbers.add(i)
}
return numbers.toList()
}
fun main(args: Array<String>){
val readOnlyNumbers: List<Int> = addEl(mutableListOf())
println(readOnlyNumbers.size)
}
or simply
fun createEl(): List<Int> {
return (1..10).toList()
}
fun main(args: Array<String>){
val readOnlyNumbers = createEl()
println(readOnlyNumbers.size)
}
To answer this part:
Could you give an example to this quotation:
A key thing to keep in mind when working with collection interfaces is that read-only collections aren’t necessarily immutable. If you’re working with a variable that has a read-only interface type, this can be just one of the many references to the same collection. Other references can have a mutable interface type
This is simply talking about situations like this:
val mutableList: MutableList<Int> = mutableListOf<Int>()
val list: List<Int> = mutableList
println(list) // []
mutableList.add(0)
println(list) // [0]
Even though list has type List<Int> and not MutableList<Int>, its contents have changed.
Note that this is an example of
I want to write a function that adds elements to some collection, while creating a val someCollection: List <> in the fun main().
as well, but I wouldn't recommend writing this code; go with one of JB Nizet's versions.