how do you assign a non-generic Map to a generic Map in flow type - flowtype

I'm trying to assign a non-generic map to a generic map, but flow complains that the value is incompatible. Is there anyway around this. Look at m4 and m5 in the example below.
interface Person {
name: string;
}
type Doctor = {
name: string,
license: string,
}
var d:Doctor = {
name: 'Sam',
license: 'PHD'
};
var p: Person = d;
// It is possible to create a generic array where each element
// implements the interface Person
const a: Array<Person> = [d];
// As a Map, it appears you cannot the value cannot be generic array
let m2: Map<string, Array<Doctor>> = new Map<string, Array<Doctor>> ();
let m3: Map<string, Array<Person>> = m2;
// As a Map, it appears that value cannot be a generic object
let m4: Map<string, Doctor> = new Map<string, Doctor> ();
m4.set('bob', d);
let m5: Map<string, Person> = m4;
It errors with the following statement
28: let m5: Map<string, Person> = m4;
^ Cannot assign `m4` to `m5` because property `license` is missing in `Person` [1] but exists in `Doctor` [2] in type argument `V` [3]. [prop-missing]
Flow

This is failing because it would be valid to do
m5.set("foo", { name: "foo" });
since that is a valid Person and that would corrupt m4 since it no longer contains Doctor objects.
For your code to work, you m5 needs to be read-only, and m3 needs to be read-only with read-only arrays, e.g.
let m3: $ReadOnlyMap<string, $ReadOnlyArray<Person>> = m2;
and
let m5: $ReadOnlyMap<string, Person> = m4;
(Flow Try)

Related

Cannot get hash map value as ArrayList

I have a data class called Product and its object are stored in an ArrayList.
Then I am creating an hashmap val prodInCartMap: HashMap<String, ArrayList<Product> = HashMap()
I am adding the array list to the hash map prodInCartMap["prodInCart"] = list, where list is val list: ArrayList<Product> = ArrayList()
Then I am uploading the data to Cloud Firestore.
When I am getting the data this is the code:
fun getHashMapActivity(uid: String, activity: CartActivity){
FirebaseFirestore.getInstance().collection(Constants.CART_COLLECTION)
.document(uid)
.get()
.addOnSuccessListener {
val map: HashMap<String, Any> = it.data as HashMap<String, ArrayList<Product>>
val temp = map["prodInCart"] as ArrayList<Product>
for (i in temp.indices){
val product: Product = temp[i] as Product
}
}
}
While executing the for loop I get this error:
java.lang.ClassCastException: java.util.HashMap cannot be cast to
com.example.grocerystore.models.Product
Why is it still a hash map even after converting it into ArrayList?
Here is the screenshot of my database:
Whenever I hover my mouse over: val cart: HashMap<String, ArrayList<Product>> = it.data as HashMap<String, ArrayList<Product>>
I get this warning:
Unchecked cast: (Mutable)Map<String!, Any!>? to
kotlin.collections.HashMap<String, ArrayList /* =
ArrayList /> / = java.util.HashMap<String,
ArrayList> */
My product class:
data class Product(
var prod_name: String = "",
var prod_image: Int = -1,
var prod_desc: String = "",
var prod_price: Int = -1,
var prod_tags: ArrayList<String> = ArrayList(),
var kgOrPack: Boolean = true //true: pack, false: kg
): Serializable
You are getting the following error:
java.lang.ClassCastException: java.util.HashMap cannot be cast to com.example.grocerystore.models.Product
Because you are trying to loop through an ArrayList that contains "HashMap" objects and not "Product" objects. Since there is no inheritance relationship between these classes, the cast is actually not possible, hence the error. There are two ways in which you can solve this.
The hard way is to iterate the HashMap objects and create "Product" objects yourself, or, the easy way is to map the array of Product objects into a List as explained in the following article:
How to map an array of objects from Cloud Firestore to a List of objects?

Map getter isn't defined for Map<String, String>

I am new on Dart, I want to map a list of objects to only get the attribute firstName, but I am getting that error:
Error: The getter 'firstName' isn't defined for the class 'Map<String, String>'.
- 'Map' is from 'dart:core'.
void main() {
var employes = [
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
];
var mapFirstName = employes.map((detail) { return {"firstName": detail.firstName}});
print(mapFirstName);
}
How I can fix it?
In Dart, you have to use bracket notation instead of the dot notation to access the values of the keys in the map.
Your code should look like:
var mapFirstName = employes.map((detail) => {"firstName": detail["firstName"]});

Java8 Create Map grouping by key contained in values

I have the following two lists of String:
{APPLE, ORANGE, BANANA} //call it keyList
{APPLE123, ORANGEXXX, 1APPLE, APPLEEEE} //call it valueList
Desired output is an HashMap<String, List<String>> like this:
<APPLE, {APPLE123, 1APPLE, APPLEEEE}>
<ORANGE, {ORANGEXXX}>
<BANANA, {}> //also <key, null> is accepted
I have implemented this solution(it works)
HashMap<String, List<String>> myMap = new HashMap<>();
keyList.forEach(key -> {
List<String> values = valueList.stream()
.filter(value -> value.contains(key))
.collect(Collectors.toList());
myMap.put(key, values);
});
Given the assumption that a value is related to only one key (it's a constraint of my domain), is this the best solution in java8 , in terms of performance and/or code cleaning ?
Can it be tuned in some way?
If you can safely assume that each value is associated with a key, and only one key, you can go into the following direction:
Pattern p = Pattern.compile(String.join("|", keyList));
Map<String, List<String>> map = valueList.stream()
.collect(Collectors.groupingBy(s -> {
Matcher m = p.matcher(s);
if(!m.find()) throw new AssertionError();
return m.group();
}));
map.forEach((k,v) -> System.out.println(k+": "+v));
If the keys may contain special characters which could get misinterpreted as regex constructs, you can change the preparation code to
Pattern p = Pattern.compile(
keyList.stream().map(Pattern::quote).collect(Collectors.joining("|")));
The collect operation does only create the groups for existing values. If you really need all keys to be present, you can use
Map<String, List<String>> map = valueList.stream()
.collect(Collectors.groupingBy(s -> {
Matcher m = p.matcher(s);
if(!m.find()) throw new AssertionError();
return m.group();
},
HashMap::new, // ensure mutable map
Collectors.toList()
));
keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));
or
Pattern p = Pattern.compile(
keyList.stream().map(Pattern::quote)
.collect(Collectors.joining("|", ".*(", ").*")));
Map<String, List<String>> map = valueList.stream()
.map(p::matcher)
.filter(Matcher::matches)
.collect(Collectors.groupingBy(m -> m.group(1),
HashMap::new, // ensure mutable map
Collectors.mapping(Matcher::group, Collectors.toList())
));
keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));

How to transform a Custom Object List by excluding particular property in Kotlin?

How to transform a List into a new List by excluding a property in T.
For instance if User data class has 10 properties, I need to transform List into a new List without one particular property in User . New List like List
data class User(val name: String, val age: Int)
var userList = mutableListOf<User>()
var nameList= userList.map { it.name }
If a List to be created without property 'age'. Like
var withoutAgeList
In your first example:
var userList = mutableListOf<User>()
var nameList= userList.map { it.name }
The question "What's the type of nameList?" has a simple answer: List<String>. So let me ask you a similar question: What's the type of withoutAgeList? The answer to that question informs the answer to your question.
Perhaps a user without the age property is a separate AgelessUser class, meaning withoutAgeList is of type List<AgelessUser>. In that case, I suggest either a constructor or a factory function that builds AgelessUser from User, and you want one of these:
val withoutAgeList = userList.map { AgelessUser(it) } // constructor
val withoutAgeList = userList.map { agelessUserOf(it) } // factory
Alternatively, maybe the age property in User is nullable and immutable, and you want to represent users without an age as a regular User where age=null. In this case, you could copy the Users and override the age field
// TODO: pass all the other fields too
val withoutAgeList = userList.map { User(it.name, null) }
Assuming Users is a data class, we can avoid explicitly naming all fields by making use of copy():
val withoutAgeList = userList.map { it.copy(age = null) }
Maybe the age property is nullable and mutable — and you actually want to change the users in place instead of copying them. This is somewhat risky and I don't advocate doing it this way unless you really know what you're doing though.
userList.forEach { it.age = null }
// They're actually the same list!
val withoutAgeList = userList
In such a simple case you can map a list of Users into a list of strings:
val names: List<String> = userList.map(User::name)
Or you can declare a DTO and map into the latter:
class UserWithoutAge(val name: String)
val usersWithoutAge: List<UserWithoutAge> = userList.map { UserWithoutAge(it.name) }
P.S. you don't have to write an explicit type
You can use the Object Oriented approach:
data class User(val name: String, val age: Int)
data class UserNoAge(var name: String) {
constructor(user: User) : this(user.name)
}
var userList = listOf(User("John", 25), User("Jane", 30))
var userNoAge: List<UserNoAge> = mutableListOf<UserNoAge>()
userNoAge = userList.map{ UserNoAge(it) }
println(userNoAge) // [UserNoAge(name=John), UserNoAge(name=Jane)]

How to convert/transform a collection to another collection by element's property?

If I have a collection of an object in Kotlin, is there a quick way to get a collection of a certain property of those objects? I looked at a list of collection operations for Kotlin, but nothing stood out for me (but I may have overlooked something)
In python it would be akin to:
[person.name for person in persons]
And I'd prefer to use a collections function instead of doing:
var nameMap = mutableListOf<String>()
persons.forEach{person -> nameMap.add(person.name)}
I'm pretty lacking in knowledge of filtering/lambda functions and anything other than list comprehension, so apologies if this is a simple question
it's easy to do in Kotlin:
// v--- the variable type can be removed
var nameMap: MutableList<String> = persons.map { it.name }.toMutableList();
IF you want an immutable List, it can simplify as below:
// v--- the variable type can be removed
var nameMap: List<String> = persons.map { it.name };
OR using function reference expression instead:
var nameMap = persons.map(Person::name);
If you want to map model then do this.
var nameMap: List<Model> = persons.map { Model(it.name,it.number) };
Option 1:
inline fun <reified T, Y> MutableList<T>.listOfField(property: KMutableProperty1<T, Y?>): MutableList<Y> {
val yy = ArrayList<Y>()
this.forEach { t: T ->
yy.add(property.get(t) as Y)
}
return yy
}
Usage:
val serviceIds = services.listOfField(ServiceModel::id)
Option 2:
var serviceIds: MutableList<String> = services.map { it.id }.toMutableList()
Option 3:
Function Reference
var serviceIds = services.map(Person::id)

Resources