I am reading from 3 separate .json files via futures and want to concatenate them into one list. However, I dont't know how to, since .add and + seem to not be defined for future Lists. I also struggle to use then for further concatenation.
I want to return a Future with values from all 3 json files.
Code:
Future<List<Furniture>> getAllHousewares() {
if (_allHousewares != null) {
return _allHousewares;
}
_allHousewares = rootBundle.loadString("res/raw/housewares.json").then((json) =>
(jsonDecode(json) as List).map((houseware) => Furniture.fromJson(houseware)).toList());
_allWallmounteds = rootBundle.loadString("res/raw/Wall-mounted.json").then((json) =>
(jsonDecode(json) as List).map((wallmounted) => Furniture.fromJson(wallmounted)).toList());
_allMiscellaneouss = rootBundle.loadString("res/raw/miscellaneous.json").then((json) =>
(jsonDecode(json) as List).map((miscellaneous) => Furniture.fromJson(miscellaneous)).toList());
return _allHousewares;
}
For this case, the best way to handle it is to use the wait static method of the Future class. The method:
Waits for multiple futures to complete and collects their results.
Since all of your loaded JSON strings contain the same datatype, you can handle them all the same way with loops which leads to cleaner code. This code uses await, which is just syntactic sugar for .then.
After the data for each json is processed, it's concatenated with expand, though it could have been easily done with the + operator as well.
Example:
Future<List<Furniture>> getAllHousewares() async {
if (_allHousewares != null) {
return _allHousewares;
}
Future _allHousewares = rootBundle.loadString("res/raw/housewares.json");
Future _allWallmounteds = rootBundle.loadString("res/raw/Wall-mounted.json");
Future _allMiscellaneouss = rootBundle.loadString("res/raw/miscellaneous.json");
List combined = await Future.wait([_allHousewares, _allWallmounteds, _allMiscellaneouss ]);
List<List<Furniture>> tempFurniture= List();
for(String json in combined) {
tempFurniture.add((jsonDecode(json) as List).map((thingInList) => Furniture.fromJson(thingInList)).toList());
}
List<Furniture> furniture = tempFurniture.expand((item) => item).toList();
return furniture;
}
Related
I have a function which creates a sublist from a large(very large list). After creating this list, the function goes on treating it (deleting duplicates, sorting...).
As long as the list was not too big, it worked fine. But now, I get "The Getter length was called on null". I suppose, it's because the second part of the function (after the loop) starts before the sublist is completed... so it doesn't work...
How can we force the function to wait for the loop to be over to continue the rest of the treatment ?
Is it with Async /Await ? Or can we do something like "While... something is not over...", or "As soon as something is done... do that" ? (My suggestions might be naive, but I am a beginner...)
Here is the code :
List themeBankFr() {
List<Map> themeBankFr = [];
for (Word word in wordBank) {
for (Thematique wordTheme in word.theme) {
themeBankFr.add({
'themeFr': wordTheme.themeFr,
'image': wordTheme.image,
});
}
}
// convert each item to a string by using JSON encoding
final jsonList = themeBankFr.map((item) => jsonEncode(item)).toList();
// using toSet - toList strategy
final uniqueJsonList = jsonList.toSet().toList();
// convert each item back to the original form using JSON decoding
final result = uniqueJsonList.map((item) => jsonDecode(item)).toList();
// sort the list of map in alphabetical order
result.sort((m1, m2) {
var r = m1['themeFr'].compareTo(m2['themeFr']);
if (r != 0) return r;
return m1['image'].compareTo(m2['image']);
});
return result;
}
i think i have a good answer that may helps you and it will as following
first create another function to do the work of for loops and this function returns a future of list that you need like below
Future<List<Map>> futureList(List wordBank){
List<Map> themeBankFr = [];
for (Word word in wordBank) {
for (Thematique wordTheme in word.theme) {
themeBankFr.add({
'themeFr': wordTheme.themeFr,
'image': wordTheme.image,
});
}
}
return Future.value(themeBankFr);
}
after that you can use this function inside your code and use it as async await and now you will never run the below lines before you return this array like below
List themeBankFr() async {
List<Map> themeBankFr = await futureList(wordBank);
// convert each item to a string by using JSON encoding
final jsonList = themeBankFr.map((item) => jsonEncode(item)).toList();
// using toSet - toList strategy
final uniqueJsonList = jsonList.toSet().toList();
// convert each item back to the original form using JSON decoding
final result = uniqueJsonList.map((item) => jsonDecode(item)).toList();
// sort the list of map in alphabetical order
result.sort((m1, m2) {
var r = m1['themeFr'].compareTo(m2['themeFr']);
if (r != 0) return r;
return m1['image'].compareTo(m2['image']);
});
return result;
}
i think this will solve your problem and i hope this useful for you
I'm trying to manipulate properties in a GroupBy clause to be used in a dictionary:
var lifeStages = await _dbContext.Customers
.GroupBy(x => GetLifeStage(x.DoB))
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
I'm expecting results like
adolescent: 10,
adult: 15,
senior: 12 etc
But getting error:
Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly
by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Offcourse I can't combine ToDictionary() with any of the mentioned calls, and splitting up the query did not resolve the issues or taught my anything)
I've tried with making GetLifeStage() static and async, no difference there as well. The method gets called, performs what it needs to do, and still GroupBy can't be translated
If I leave out the Select() part and work with the Key of the GroupBy, same error:
"...could not be translated."
I saw an error too that said I couldn't combine a GroupBy() with a ToDictionary() during try-outs, but doesn't seem to pop up atm.
As I'm running out of ideas, all suggestions are welcome!
update:
private LifeStage GetLifeStage(DateTimeOffset doB)
{
var ageInMonths = Math.Abs(12 * (doB.Year - DateTimeOffset.UtcNow.Year) + doB.Month - DateTimeOffset.UtcNow.Month);
switch (ageInMonths)
{
case < 216:
return LifeStage.Adolescent;
case < 780:
return LifeStage.Adult;
case >= 780:
return LifeStage.Senior;
}
}
The problem is the usage of the custom GetLifeStage method inside the GroupBy expression. Custom methods cannot be translated to SQL because the query translator code has no way to know what is inside that method. And it cannot be called because there are no objects at all during the translation process.
In order to make it translatable, you have to replace the custom method call with its body, converted to translatable expression - basically something which can be used as expression bodied method. You can't use variables and switch, but you can use conditional operators. Instead of variable, you could use intermediate projection (Select).
Here is the equivalent translatable query:
var lifeStages = await _dbContext.Customers
.Select(c => new { Customer = c, AgeInMonths = Math.Abs(12 * (c.DoB.Year - DateTimeOffset.UtcNow.Year) + c.DoB.Month - DateTimeOffset.UtcNow.Month) })
.GroupBy(x => x.AgeInMonths < 216 ? LifeStage.Adolescent : x.AgeInMonths < 780 ? LifeStage.Adult : LifeStage.Senior)
// the rest is the same as original
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
This the code I have right now (I'm using Mobx):
#observable
ObservableMap dates = ObservableMap();
#action
void getDate() {
final Map obj = {};
final map = item['dates'].map((date) {
DateTime key = DateTime.parse(date['date']);
obj.addAll({
key: ['list']
});
});
// print(map);
dates.addAll(obj);
}
I have function to call query and call getDate function.
At my UI I just call the dates but it won't return any value. The value only return if the print syntax not comment.
Any solutionn?
You are using the map method to do something for each element of item['dates']. That doesn't work because the map operation is lazy and doesn't do anything until you start using the result. You can call .toList() on the result to make it do all the computations, but that's a roundabout way to do it.
Use forEach instead to eagerly do something for each element, or, even better, use a for-in loop:
item['dates'].forEach((date) { ... });
or
for (var date in item['dates']) {
var key = DateTime.parse(date['date']);
obj.addAll({key: ['list']});
// or just:
// obj[key] = ['list'];
}
As I understand, ordered assertions in FakeItEasy 2 are done like this (from the docs):
// Assert
A.CallTo(() => unitOfWorkFactory.BeginWork()).MustHaveHappened()
.Then(A.CallTo(() => usefulCollaborator.JustDoIt()).MustHaveHappened())
.Then(A.CallTo(() => unitOfWork.Dispose()).MustHaveHappened());
Now, suppose I have a collection and for each item in this collection I want to assert that a call was made to a faked object. What is the best approach to assert the calls were made in the correct order?
I came up with this, but don't really like it:
IOrderableCallAssertion ioca = null;
foreach (var item in items.OrderBy(i => i.Id)
{
var itemUnderTest = item;
if (ioca == null)
{
ioca = A.CallTo(() => fakeObject.Handle(itemUnderTest, otherArgument)).MustHaveHappened(Repeated.Exactly.Once);
}
else
{
ioca = ioca.Then(A.CallTo(() => fakeObject.Handle(itemUnderTest, otherArgument)).MustHaveHappened(Repeated.Exactly.Once));
}
}
That looks about right to me. Of course, you could inline itemUnderTest and pull MustHaveHappened outside of the two if branches.
And you could always hide this in a convenience method.
An alternative: use Invokes to capture the fakes as the calls come in and later compare them against a list.
I read file from HDFS, which contains x1,x2,y1,y2 representing a envelope in JTS.
I would like to use those data to build STRtree in foreach.
val inputData = sc.textFile(inputDataPath).cache()
val strtree = new STRtree
inputData.foreach(line => {val array = line.split(",").map(_.toDouble);val e = new Envelope(array(0),array(1),array(2),array(3)) ;
println("envelope is " + e);
strtree.insert(e,
new Rectangle(array(0),array(1),array(2),array(3)))})
As you can see, I also print the e object.
To my surprise, when I log the size of strtree, it is zero! It seems that insert method make no senses here.
By the way, if I write hard code some test data line by line, the strtree can be built well.
One more thing, those project is packed into jar and submitted in the spark-shell.
So, why does the method in foreach not work ?
You will have to collect() to do this:
inputData.collect().foreach(line => {
... // your code
})
You can do this (for avoiding collecting all data):
val pairs = inputData.map(line => {
val array = line.split(",").map(_.toDouble);
val e = new Envelope(array(0),array(1),array(2),array(3)) ;
println("envelope is " + e);
(e, new Rectangle(array(0),array(1),array(2),array(3)))
}
pairs.collect().foreach(pair => {
strtree.insert(pair._1, pair._2)
}
Use .map() instead of .foreach() and reassign the outcome.
Foreach does not return the outcome of applyied function. It can be used for sending data somewhere, storing to db, printing, and so on.