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?
Related
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)
Porting an App from Swift to Flutter. In my App, I have a class, MyClass, and am dealing with a list of about 250 instances. At various times, I need to group the objects based on a particular property.
In Swift, I was able to create a grouped list of my objects like so:
var groupedList = Dictionary<String, Array<MyClass>>()
I was then able to loop through my list of objects, and assign items to the right Array as necessary. I thought it might work to make a Map of Lists in Flutter like this:
Map groupedList = Map<String, List<MyClass>>();
Then I could loop through the items, test the property, create a Map entry for each unique value and append the item to the correct List:
for (var item in listOfObjects) {
if (!groupedList.containsKey(item.someproperty)) {
List<MyClass> sublist = [];
groupedList[item.someproperty] = sublist;
}
groupedList[item.someproperty].add(item);
}
What I get, however, is a Map with all the correct Keys, but each List contains only one instance of MyClass, rather than an actual List of MyClasses.
There's a more succinct syntax using putIfAbsent. This gives the results you expect:
void main() {
var groupedList = <String, List<MyClass>>{};
var listOfObjects = <MyClass>[
MyClass('Europe', 'France'),
MyClass('Europe', 'Germany'),
MyClass('Europe', 'Italy'),
MyClass('North America', 'USA'),
MyClass('Asia', 'Japan'),
MyClass('Asia', 'China'),
];
for (var item in listOfObjects) {
groupedList.putIfAbsent(item.someProperty, () => <MyClass>[]).add(item);
}
print(groupedList);
}
class MyClass {
String someProperty;
String someValue;
MyClass(this.someProperty, this.someValue);
#override
String toString() => '$someProperty->$someValue';
}
I'm trying to translate my app from Java to Kotlin.
I'm managing database with AnKo SQLite
All is OK except listviews with CursorLoaders : I can't find how to replace CursorLoader while using AnKo SQLite.
(and same problem with expandableListViews)
Can somebody help me please?
OK, here is my solution... I don't know if it is the best :
create a new kotlin class "MyCursorLoader" that extends CursorLoader
set the primary constructor like this :
class MyCursorLoader(
mContext: Context,
val mTableName: String,
var mProjection: Array<String>? = null,
var mSelection: String = "1",
var mSelectionArgs: Array<String> = emptyArray(),
var mGroupBy: String = MySqlHelper.ID,
var mHaving: String = "",
var mSortOrder: String = "${MySqlHelper.ID} ASC",
var mLimit: String = "",
var mDistinct: Boolean = true
): CursorLoader(mContext) {
val mObserver: Loader<Cursor>.ForceLoadContentObserver = Loader<Cursor>(mContext).ForceLoadContentObserver()
var mCancellationSignal: CancellationSignal? = null
override the OnLoadInBackground method with te same code than built-in one, just replacing the val cursor = ContentResolverCompat.query(... line with :
val cursor = MySqlHelper.instance.readableDatabase.query(
mDistinct, mTableName, mProjection, mSelection, mSelectionArgs, mGroupBy, mHaving, mSortOrder, mLimit, mCancellationSignal)
So no need to recreate a dataprovider in manifest, no need to deal with Uri's... I can use MyCursorLoader exactly like built-in CursorLoader, calling it like this :
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
when (id) {
DAY_HEADER_LOADER ->
return MyCursorLoader(mContext, TABLE_EVENTS, arrayOf(ID, DAY), mGroupBy = DAY, mSortOrder = "$DAY DESC")
...
}
}
Let me know if ther is a better solution.
Hope that can help.
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 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)]