Response duplicated but the count shows as 1 - loops

Using Dynamoose ORM with Serverless. I have a scenario where I'm finding user information based on recommendation.
The response is as follows
{
"data": {
"results": [
{
"specialTip": "Hello World",
"recommendation": "Huli ka!",
"poi": {
"uuid": "poi_555",
"name": "Bukit Panjang",
"images": [
{
"url": "facebook.com",
"libraryUuid": "2222",
"uuid": "9999"
}
]
},
"uuid": "i_8253578c-600d-4dfd-bd40-ce5b9bb89067",
"headline": "Awesome",
"dataset": "attractions",
"insiderUUID": "i_c932e85b-0aee-4462-b930-962f555b64bd",
"insiderInfo": [
{
"gender": "m",
"funFacts": [
{
"type": "knock knock!",
"answer": "Who's there?"
}
],
"profileImage": "newImage.jpg",
"shortDescription": "Samething",
"fullDescription": "Whatever Description",
"interests": [
"HELLO",
"WORLD"
],
"tribes": [
"HELLO",
"WORLD"
],
"uuid": "i_c932e85b-0aee-4462-b930-962f555b64bd",
"personalities": [
"HELLO",
"WORLD"
],
"travelledCities": [
"HELLO",
"WORLD"
]
}
]
},
{
"specialTip": "Hello World",
"recommendation": "Huli ka!",
"poi": {
"uuid": "poi_555",
"name": "Bukit Panjang",
"images": [
{
"url": "facebook.com",
"libraryUuid": "2222",
"uuid": "9999"
}
]
},
"uuid": "i_8253578c-600d-4dfd-bd40-ce5b9bb89067",
"headline": "Awesome",
"dataset": "attractions",
"insiderUUID": "i_c932e85b-0aee-4462-b930-962f555b64bd",
"insiderInfo": [
{
"gender": "m",
"funFacts": [
{
"type": "knock knock!",
"answer": "Who's there?"
}
],
"profileImage": "newImage.jpg",
"shortDescription": "Samething",
"fullDescription": "Whatever Description",
"interests": [
"HELLO",
"WORLD"
],
"tribes": [
"HELLO",
"WORLD"
],
"uuid": "i_c932e85b-0aee-4462-b930-962f555b64bd",
"personalities": [
"HELLO",
"WORLD"
],
"travelledCities": [
"HELLO",
"WORLD"
]
}
]
}
],
"count": 1
},
"statusCode": 200
}
Not sure where I'm going wrong as the items in the response seems to be duplicated but the count is 1.
Here is the code
module.exports.index = (_event, _context, callback) => {
Recommendation.scan().exec((_err, recommendations) => {
if (recommendations.count == 0) {
return;
}
let results = [];
recommendations.forEach((recommendation) => {
Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => {
if (insider.count == 0) {
return;
}
recommendation.insiderInfo = insider;
results.push(recommendation);
});
});
const response = {
data: {
results: results,
count: results.count
},
statusCode: 200
};
callback(null, response);
});
};

EDIT: My previous code ignored the fact that your "Insider" query is asynchronous. This new code handles that and matches your edit.
const async = require('async'); // install async with 'npm install --save async'
[...]
module.exports.index = (_event, _context, callback) => {
Recommendation.scan().exec((_err, recommendations) => {
if (_err) {
console.log(_err);
return callback(_err);
}
if (recommendations.count == 0) {
const response = {
data: {
results: [],
count: 0
},
statusCode: 200
};
return callback(null, response);
}
let results = [];
async.each(recommendations, (recommendation, cb) => { // We need to handle each recommendation asynchronously...
Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => { // because this is asynchronous
if (_err) {
console.log(_err);
return callback(_err);
}
if (insider.count == 0) {
return cb(null);
}
recommendation.insiderInfo = insider;
results.push(recommendation);
return cb(null);
});
}, (err) => { // Once all items are handled, this is called
if (err) {
console.log(err);
return callback(err);
}
const response = { // We prepare our response
data: {
results: results, // Results may be in a different order than in the initial `recommendations` array
count: results.count
},
statusCode: 200
};
callback(null, response); // We call our main callback only once
});
});
};
Initial (partly incorrect) answer, for reference.
You are pushing the result of your mapping into the object that you are currently mapping and callback is called more than once here. That's a pretty good amount of unexpected behavior material.
Try the following:
let results = [];
recommendations.forEach((recommendation) => {
Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => {
if (insider.count == 0) {
return;
}
recommendation.insiderInfo = insider;
results.push(recommendation);
});
});
let response = {
data: {
results: results,
count: results.count
},
statusCode: 200
};
callback(null, response);

Related

JSON dot notation returning different from JSON api request [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 14 days ago.
Using dot notation to access a JSON response is returning different values than what the API response is saying in my browser
I tried to access a JSON response from an axios request like so:
const response = await axios.get('https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=en-US&country=US&allowCountries=US');
console.log(response.data.data.Catalog.searchStore.elements.promotions)
Instead of getting a response something similar to this in JSON:
{
"promotions": {
"promotionalOffers": [
{
"promotionalOffers": [
{
"startDate": "2023-02-02T16:00:00.000Z",
"endDate": "2023-02-09T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 0
}
}
]
}
],
"upcomingPromotionalOffers": []
}
}
I simply get this from the console log:
undefined
undefined
undefined
undefined
undefined
I might not be using dot notation to access it correctly but I have no idea. You can view the JSON response from the browser here: https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=en-US&country=US&allowCountries=US
#1 Getting data from the server by axios.
The elements start [ and end ] is an array data.
(* I copy/paste from Browser to VS code after access URL, saved JSON data)
VS code can expand/collapse data by clicking down arrow.
#2 Filter only promotions
It is one of Key values of array elements
You can filter by Array map()
Simple example for getting title only
const titles = elements.map(item => { return { title : item.title } } )
console.log(JSON.stringify(titles, null, 4))
[
{
"title": "Borderlands 3 Season Pass"
},
{
"title": "City of Gangsters"
},
{
"title": "Recipe for Disaster"
},
{
"title": "Dishonored®: Death of the Outsider™"
},
{
"title": "Dishonored - Definitive Edition"
}
]
So this code will works for getting promotions
const axios = require('axios')
const getPromotions = async (data) => {
try {
const response = await axios.get('https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=en-US&country=US&allowCountries=US');
return Promise.resolve(response.data)
} catch (error) {
return Promise.reject(error)
}
};
getPromotions()
.then(result => {
const promotions = result.data.Catalog.searchStore.elements.map(item => { return { promotions : item.promotions } } )
console.log(JSON.stringify(promotions, null, 4))
})
.catch(error => {
console.log(error.message)
});
Result
$ node get-data.js
[
{
"promotions": {
"promotionalOffers": [
{
"promotionalOffers": [
{
"startDate": "2023-01-26T16:00:00.000Z",
"endDate": "2023-02-09T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 30
}
},
{
"startDate": "2023-01-26T16:00:00.000Z",
"endDate": "2023-02-09T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 30
}
}
]
}
],
"upcomingPromotionalOffers": []
}
},
{
"promotions": {
"promotionalOffers": [
{
"promotionalOffers": [
{
"startDate": "2023-02-02T16:00:00.000Z",
"endDate": "2023-02-09T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 0
}
}
]
}
],
"upcomingPromotionalOffers": []
}
},
{
"promotions": {
"promotionalOffers": [],
"upcomingPromotionalOffers": [
{
"promotionalOffers": [
{
"startDate": "2023-02-09T16:00:00.000Z",
"endDate": "2023-02-16T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 0
}
}
]
}
]
}
},
{
"promotions": {
"promotionalOffers": [
{
"promotionalOffers": [
{
"startDate": "2023-02-02T16:00:00.000Z",
"endDate": "2023-02-09T16:00:00.000Z",
"discountSetting": {
"discountType": "PERCENTAGE",
"discountPercentage": 0
}
}
]
}
],
"upcomingPromotionalOffers": []
}
},
{
"promotions": null
}
]

ReactHook adding array of an array state without duplicating the key

I am trying to add data grouping by the unit name for showing functionality
const [allData , setAllData]= useState([{'unit':'' , data:[]}])
useEffect(async () => {
await axios.get(`${BACKEND_URL}/data`).then(res => {
res.data.map(elem => {
setAllData(prev =>[...prev , { 'unit': elem.unitName, 'data': [elem.lessonName] }]);
});
});
}, []);
the result is duplicating the key for the subarray which is "unit" for my exampl:
[
{
"unit": "unit 1",
"data": [
"LO1"
]
},
{
"unit": "unit 2",
"data": [
"LO2"
]
},
{
"unit": "unit 3",
"data": [
"LO3"
]
},
{
"unit": "unit 1",
"data": [
"LO15"
]
}
]
Try like that, if find unique property unit rewrite data or push new element to array
useEffect(async () => {
await axios.get(`${BACKEND_URL}/data`).then(res => {
setAllData((prev) => {
let result = [...prev];
res.data.forEach(({ unitName: unit, lessonName }) => {
const index = result.findIndex((elem) => elem.unit === unit);
if (index >= 0) {
result[index].data = [...result[index].data, lessonName]
} else {
result.push({unit, data: [lessonName]});
}
});
return result;
});
});
}, []);

How to delete all sub array

In my MongoDB database, I have a collection 'produits' with documents like this
{
"_id": {
"$oid": "6048e97b4a5f000096007505"
},
"modeles": [
{
"id": "OppoA3",
"pieces": [
{
"id": "OppoA3avn"
},
{
"id": "OppoA3bat"
}]
]
},
{
"id": "OppoA1",
"pieces": [
{
"id": "OppoA1avn",
},
{
"id": "OppoA1batt",
}
]
}
]
}
How can I delete all modeles.pieces from all my documents.
I managed to delete with a filter on modeles.id but with that code but not on all the collection
db.produits.update(
{marque_id:'OPPO', 'modeles.id':'RENOZ'},
{$set:
{
'modeles.$.pieces': []
}
}
, { multi : true }
)
I would like all documents like this finally
{
"_id": {
"$oid": "6048e97b4a5f000096007505"
},
"modeles": [
{
"id": "OppoA3",
"pieces": []
},
{
"id": "OppoA1",
"pieces": []
}
]
}
Thank you for your help.
I have done a javascript loop like this, but i think it's not best practice
async removePieces(){
var doc
try {
doc = await produitModel.find()
for (var produit of doc) {
for (var modele of produit.modeles) {
const filter = {'marque_id': produit.marque_id, 'modeles.id': modele.id}
const set = {
$set: {
'modeles.$.pieces': []
}
}
await db.collection('produits').updateOne(filter, set)
}
}
console.log('removePieces() ==> Terminé')
} catch(err) {
console.log(err)
}
}
db.produits.update({
modeles: {//This is because your second document will create failure otherwise
$exists: true
}
},
{
$set: {
"modeles.$.pieces": []
}
},
{
multi: true
})

How do I sort this array by date?

I'm trying to sort the dates from this external API in my latestResults array by latest on top to oldest on bottom but can't seem to figure out how.
Right now they're displayed with the oldest date first and it's working fine, but it's in the wrong order for me.
I tried using result in latestResults.reverse() but that just reverses the 7 items currently in the array.
HTML:
<div v-for="result in latestResults" v-bind:key="result.latestResults">
<small">{{ result.utcDate }}</small>
</div>
Script:
<script>
import api from '../api'
export default {
data () {
return {
latestResults: [],
limit: 7,
busy: false,
loader: false,
}
},
methods: {
loadMore() {
this.loader = true;
this.busy = true;
api.get('competitions/PL/matches?status=FINISHED')
.then(response => { const append = response.data.matches.slice(
this.latestResults.length,
this.latestResults.length + this.limit,
this.latestResults.sort((b, a) => {
return new Date(b.utcDate) - new Date(a.utcDate);
})
);
setTimeout(() => {
this.latestResults = this.latestResults.concat(append);
this.busy = false;
this.loader = false;
}, 500);
});
}
},
created() {
this.loadMore();
}
}
</script>
The JSON where I'm getting matches like this that has utcDate:
{
"count": 205,
"filters": {
"status": [
"FINISHED"
]
},
"competition": {
"id": 2021,
"area": {
"id": 2072,
"name": "England"
},
"name": "Premier League",
"code": "PL",
"plan": "TIER_ONE",
"lastUpdated": "2021-02-01T16:20:10Z"
},
"matches": [
{
"id": 303759,
"season": {
"id": 619,
"startDate": "2020-09-12",
"endDate": "2021-05-23",
"currentMatchday": 22
},
"utcDate": "2020-09-12T11:30:00Z",
"status": "FINISHED",
"matchday": 1,
"stage": "REGULAR_SEASON",
"group": "Regular Season",
"lastUpdated": "2020-09-13T00:08:13Z",
"odds": {
"msg": "Activate Odds-Package in User-Panel to retrieve odds."
},
},

Node async map not excecuting properly inside redux action

I connect to an API which is returning data in this format:
[
{
"id": 23,
"name": "11:40AM July 2",
"airplayEnabled": false,
"airplayCodeEnabled": true,
"type": 0,
"groups": [
{
"id": 4,
"name": "Hallway",
"notes": "Any devices present in the hallway",
"_pivot_device_id": 23,
"_pivot_group_id": 4
},
{
"id": 5,
"name": "Middle school",
"notes": "In classrooms 6-8",
"_pivot_device_id": 23,
"_pivot_group_id": 5
}
],
"mac": "123456789ABC"
},
{
"id": 26,
"name": "1:54PM July 5",
"airplayEnabled": false,
"airplayCodeEnabled": true,
"type": 0,
"groups": [
{
"id": 5,
"name": "Middle school",
"notes": "In classrooms 6-8",
"_pivot_device_id": 26,
"_pivot_group_id": 5
}
],
"mac": "123456789ABC"
}
]
I want to modify each returned item and remove the excess data, so it will end up like this:
[
{
"id": 23,
"name": "11:40AM July 2",
"airplayEnabled": false,
"airplayCodeEnabled": true,
"type": 0,
"groups": [4, 5],
"mac": "123456789ABC"
},
{
"id": 26,
"name": "1:54PM July 5",
"airplayEnabled": false,
"airplayCodeEnabled": true,
"type": 0,
"groups": [5],
"mac": "123456789ABC"
}
]
I created the following code to remove the excess data:
const deleteExcessInfo = async function(group) {
if(group.id) {
return group.id;
} else {
return null;
}
}
const modGroups = async function(device) {
async.map(device.groups, deleteExcessInfo, function(err, res) {
device.groups = res;
});
return device;
}
var newDevices;
async.map(devices, modGroups, (error, results) => {
console.log("results are " + JSON.stringify(results));
});
When I execute this code in a stand-alone node program (run from the command line), I get the expected output with the excess data removed. However, it does not work when I put it in a redux action, like this:
export function getDevices() {
return function(dispatch) {
return fetch("http://my-awesome-api:1357/device", {mode: "cors"})
.then(handleErrors)
.then(json)
.then(function(data) {
async.map(data, fixGroups, async function(error, res) {
console.log("dispatching...");
console.log(JSON.stringify(res));
dispatch({
type: "GET_DEVICES",
devices: res
});
});
}).catch((err) => {
dispatch({
type: "GET_DEVICES_ERROR",
error: err
});
alert("Oh shoot we have an error " + err);
return(err);
});
}
};
I do not see "dispatching..." or the res printed to the console, so it appears the callback function is not being executed for some reason. Any ideas on why it's not working, and on how to fix it? Thanks!
What I've tried
I tried implementing a promise as Moonjsit recommended, but it did not work. I have also tried implementing a promise in my Redux action, like this:
export function getDevices() {
return function(dispatch) {
return fetch("http://localhost:1357/device", {mode: "cors"})
.then(handleErrors)
.then(json)
.then(function(data) {
return new Promise((resolve, reject) => {
async.map(data, fixGroups, (error, results) => {
console.log("results are " + JSON.stringify(results));
resolve(dispatch({
type: "GET_DEVICES",
devices: results
});
});
});
}).catch((err) => {
dispatch({
type: "GET_DEVICES_ERROR",
error: err
});
alert("Oh shoot we have an error " + err);
return(err);
});
}
};
Either way, the code inside the callback does not execute.
Hey so you have a problem in your code.
const modGroups = async function(device) {
async.map(device.groups, deleteExcessInfo, function(err, res) {
device.groups = res;
});
return device;
}
Calling function above immediately starts async.map which is asynchronus, and then returns unchanged device variable. So effectively it gives same reults as:
const modGroups = async function(device) {
return device;
}
To fix it you can eg. wrap it in Promise:
const modGroups = async function(device) {
return new Promise((resolve, reject) => {
async.map(device.groups, deleteExcessInfo, function(err, res) {
if (err) {
reject(err);
} else {
device.groups = res;
resolve(device);
});
})
}

Resources