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 need to add data to a Map or HashMap before a for...loop, add data to the Map during the for...loop and then create the document with all of the data after the loop.
In Java for Android I used:
Map<String, Object> createDoc = new HashMap<>();
createDoc.put("type", type);
createDoc.put("title", title);
for (int x = 0; x < sArray.size(); x++) {
createDoc.put("data " + x,sArray.get(x));
}
firebaseFirestoreDb.collection("WPS").add(createDoc);
My question is, how would I create the document and immediately get the ID of it to then update/merge it with the rest of the data? Or is there a way to add data to a Map in Dart?
The only thing I've found in Dart is:
Map<String, Object> stuff = {'title': title, 'type': type};
and in the for...loop:
stuff = {'docRef $x': docId};
and after the for...loop:
Firestore.instance.collection('workouts').add(stuff);
which creates a document with only the last entry from the for...loop.
I also imported dart:collection to use HashMap, but it won't let me use
Map<String, Object> newMap = new HashMap<>();
I get the error: "A value of type 'HashMap' can't be assigned to a variable of type 'Map<String, Object>'"
Thank you in advance!
An equivalent block of code to what you wrote in Java, for Dart, is:
Map<String, Object> createDoc = new HashMap();
createDoc['type'] = type;
createDoc['title'] = title;
for (int x = 0; x < sArray.length; x++) {
createDoc['data' + x] = sArray[x];
}
Of course, Dart has type inference and collection literals, so we can use a more short-hand syntax for both. Let's write the exact same thing from above, but with some more Dart (2) idioms:
var createDoc = <String, Object>{};
createDoc['type'] = type;
createDoc['title'] = title;
for (var x = 0; x < sArray.length; x++) {
createDoc['data' + x] = sArray[x];
}
OK, that's better, but still is not using everything Dart provides. We can use the map literal instead of writing two more lines of code, and we can even use string interpolation:
var createDoc = {
'type': type,
'title': title,
};
for (var x = 0; x < sArray.length; x++) {
createDoc['data$x'] = sArray[x];
}
I also imported dart:collection to use HashMap, but it won't let me
use
Map<String, Object> newMap = new HashMap<>(); I get the error: `"A value of type 'HashMap' can't be assigned to a variable of type
'Map'`"
There is no such syntax new HashMap<> in Dart. Type inference works without it, so you could just write Map<String, Object> map = new HashMap(), or like my above example, var map = <String, Object> {}, or even better, var map = { 'type': type }, which will type the map for you based on the key and value.
I hope that helps!
for (String varValue : arrayList1) {
Map<String, String> mapInstance = new HashMap<>();
val.put(KEY, VALUE);
val.put(VAR_KEY, varValue);
arrayList2.add(mapInstance);
}
Basically, I want to create a map with two entries and then add each of these maps to a list.
Final list:
{KEY,VALUE} {VAR_KEY,arrayList1.get(0)}
{KEY,VALUE} {VAR_KEY,arrayList1.get(1)}
{KEY,VALUE} {VAR_KEY,arrayList1.get(2)}
...
and so on
It seems you only need a simple map stage.
List<Map<String, String>> list = arrayList1.stream().map(t -> {
Map<String, String> map = new HashMap<>();
map.put("KEY", "VALUE");
map.put("VAR_KEY", t);
return map;
}).collect(Collectors.toList());
What is KEY and VAR_KEY? are they instance variable of some object which you are trying to put in Map from the incoming object.
However, you can try something like this :
Map result =
arrayList1.stream().collect(Collectors.toMap(Class::getKey, c -> c));
We are currently building a Map manually based on the two fields that are returned by a named JPA query because JPA 2.1 only provides a getResultList() method:
#NamedQuery{name="myQuery",query="select c.name, c.number from Client c"}
HashMap<Long,String> myMap = new HashMap<Long,String>();
for(Client c: em.createNamedQuery("myQuery").getResultList() ){
myMap.put(c.getNumber, c.getName);
}
But, I feel like a custom mapper or similar would be more performant since this list could easily be 30,000+ results.
Any ideas to build a Map without iterating manually.
(I am using OpenJPA, not hibernate)
Returning a Map result using JPA Query getResultStream
Since the JPA 2.2 version, you can use the getResultStream Query method to transform the List<Tuple> result into a Map<Integer, Integer>:
Map<Integer, Integer> postCountByYearMap = entityManager.createQuery("""
select
YEAR(p.createdOn) as year,
count(p) as postCount
from
Post p
group by
YEAR(p.createdOn)
""", Tuple.class)
.getResultStream()
.collect(
Collectors.toMap(
tuple -> ((Number) tuple.get("year")).intValue(),
tuple -> ((Number) tuple.get("postCount")).intValue()
)
);
Returning a Map result using JPA Query getResultList and Java stream
If you're using JPA 2.1 or older versions but your application is running on Java 8 or a newer version, then you can use getResultList and transform the List<Tuple> to a Java 8 stream:
Map<Integer, Integer> postCountByYearMap = entityManager.createQuery("""
select
YEAR(p.createdOn) as year,
count(p) as postCount
from
Post p
group by
YEAR(p.createdOn)
""", Tuple.class)
.getResultList()
.stream()
.collect(
Collectors.toMap(
tuple -> ((Number) tuple.get("year")).intValue(),
tuple -> ((Number) tuple.get("postCount")).intValue()
)
);
Returning a Map result using a Hibernate-specific ResultTransformer
Another option is to use the MapResultTransformer class provided by the Hibernate Types open-source project:
Map<Number, Number> postCountByYearMap = (Map<Number, Number>) entityManager.createQuery("""
select
YEAR(p.createdOn) as year,
count(p) as postCount
from
Post p
group by
YEAR(p.createdOn)
""")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(
new MapResultTransformer<Number, Number>()
)
.getSingleResult();
The MapResultTransformer is suitable for projects still running on Java 6 or using older Hibernate versions.
Avoid returning large result sets
The OP said:
But, I feel like a custom mapper or similar would be more performant
since this list could easily be 30,000+ results.
This is a terrible idea. You never need to select 30k records. How would that fit in the UI? Or, why would you operate on such a large batch of records?
You should use query pagination as this will help you reduce the transaction response time and provide better concurrency.
There is no standard way to get JPA to return a map.
see related question: JPA 2.0 native query results as map
Iterating manually should be fine. The time to iterate a list/map in memory is going to be small relative to the time to execute/return the query results. I wouldn't try to futz with the JPA internals or customization unless there was conclusive evidence that manual iteration was not workable.
Also, if you have other places where you turn query result Lists into Maps, you probably want to refactor that into a utility method with a parameter to indicate the map key property.
You can retrieve a list of java.util.Map.Entry instead.
Therefore the collection in your entity should be modeled as a Map:
#OneToMany
#MapKeyEnumerated(EnumType.STRING)
public Map<PhoneType, PhoneNumber> phones;
In the example PhoneType is a simple enum, PhoneNumber is an entity. In your query use the ENTRY keyword that was introduced in JPA 2.0 for map operations:
public List<Entry> getPersonPhones(){
return em.createQuery("SELECT ENTRY(pn) FROM Person p JOIN p.phones pn",java.util.Map.Entry.class).getResultList();
}
You are now ready to retrieve the entries and start working with it:
List<java.util.Map.Entry> phoneEntries = personDao.getPersonPhoneNumbers();
for (java.util.Map.Entry<PhoneType, PhoneNumber> entry: phoneEntries){
//entry.key(), entry.value()
}
If you still need the entries in a map but don't want to iterate through your list of entries manually, have a look on this post Convert Set<Map.Entry<K, V>> to HashMap<K, V> which works with Java 8.
This works fine.
Repository code :
#Repository
public interface BookRepository extends CrudRepository<Book,Id> {
#Query("SELECT b.name, b.author from Book b")
List<Object[]> findBooks();
}
service.java
List<Object[]> list = bookRepository.findBooks();
for (Object[] ob : list){
String key = (String)ob[0];
String value = (String)ob[1];
}
link https://codereview.stackexchange.com/questions/1409/jpa-query-to-return-a-map
Map<String,Object> map = null;
try {
EntityManager entityManager = getEntityManager();
Query query = entityManager.createNativeQuery(sql);
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
map = (Map<String,Object>) query.getSingleResult();
}catch (Exception e){ }
List<Map<String,Object>> list = null;
try {
EntityManager entityManager = getEntityManager();
Query query = entityManager.createNativeQuery(sql);
query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
list = query.getResultList();
}catch (Exception e){ }
JPA v2.2
Though I am late here, but if someone reaches here for solution, here is my custom working solution for multiple selected columns with multiple rows:
Query query = this.entityManager.createNativeQuery("SELECT abc, xyz, pqr,...FROM...", Tuple.class);
.
.
.
List<Tuple> lst = query.getResultList();
List<Map<String, Object>> result = convertTuplesToMap(lst);
Implementation of convertTuplesToMap():
public static List<Map<String, Object>> convertTuplesToMap(List<Tuple> tuples) {
List<Map<String, Object>> result = new ArrayList<>();
for (Tuple single : tuples) {
Map<String, Object> tempMap = new HashMap<>();
for (TupleElement<?> key : single.getElements()) {
tempMap.put(key.getAlias(), single.get(key));
}
result.add(tempMap);
}
return result;
}
in case java 8
there built in entry "CustomEntryClass"
since return is stream, then caller function (repoistory layer) must have #Transactional(readonly=true|false) annotation, otherwithe exception will be thrown
make sure you will use full qualified name of class CustomEntryClass...
#Query("select new CustomEntryClass(config.propertyName, config.propertyValue) " +
"from ClientConfigBO config where config.clientCode =:clientCode ")
Stream<CustomEntryClass<String, String>> getByClientCodeMap(#Param("clientCode") String clientCode);
With custom result class and a bit of Guava, this is my approach which works quite well:
public static class SlugPair {
String canonicalSlug;
String slug;
public SlugPair(String canonicalSlug, String slug) {
super();
this.canonicalSlug = canonicalSlug;
this.slug = slug;
}
}
...
final TypedQuery<SlugPair> query = em.createQuery(
"SELECT NEW com.quikdo.core.impl.JpaPlaceRepository$SlugPair(e.canonicalSlug, e.slug) FROM "
+ entityClass.getName() + " e WHERE e.canonicalSlug IN :canonicalSlugs",
SlugPair.class);
query.setParameter("canonicalSlugs", canonicalSlugs);
final Map<String, SlugPair> existingSlugs =
FluentIterable.from(query.getResultList()).uniqueIndex(
new Function<SlugPair, String>() {
#Override #Nullable
public String apply(#Nullable SlugPair input) {
return input.canonicalSlug;
}
});
using java 8 (+) you can get results as a list of array object (each column will from select will have same index on results array) by hibernate entity manger, and then from results list into stream, map results into entry (key, value), then collect them into map of same type.
final String sql = "SELECT ID, MODE FROM MODES";
List<Object[]> result = entityManager.createNativeQuery(sql).getResultList();
return result.stream()
.map(o -> new AbstractMap.SimpleEntry<>(Long.valueOf(o[0].toString()), String.valueOf(o[1])))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
The easiest and simplest way worked for me is:
String[] columns = {"id","name","salary","phone","address", "dob"};
String query = "SELECT id,name,salary,phone,address,dob from users ";
List<Object[]> queryResp = em.createNativeQuery(query).getResultList();
List<Map<String,String>> dataList = new ArrayList<>();
for(Object[] obj : queryResp) {
Map<String,String> row = new HashMap<>(columns.length);
for(int i=0; i<columns.length; i++) {
if(obj[i]!=null)
row.put(columns[i], obj[i].toString());
else
row.put(columns[i], "");
}
dataList.add(row);
}
Please refer, JPA 2.0 native query results as map
In your case in Postgres, it would be something like,
List<String> list = em.createNativeQuery("select cast(json_object_agg(c.number, c.name) as text) from schema.client c")
.getResultList();
//handle exception here, this is just sample
Map map = new ObjectMapper().readValue(list.get(0), Map.class);
Kindly note, I am just sharing my workaround with Postgres.
How about this ?
#NamedNativeQueries({
#NamedNativeQuery(
name="myQuery",
query="select c.name, c.number from Client c",
resultClass=RegularClient.class
)
})
and
public static List<RegularClient> runMyQuery() {
return entityManager().createNamedQuery("myQuery").getResultList();
}