async await not waiting for object to fill axios.get - javascript

I have an object called move_dict and am using the method get_learned_by_pokemon() to fill it. In getPokes() I call the get_learned_by_pokemon() and expect move_dict to be filled. However it is empty.
function move(){
let move_dict = {}
let db_data = JSON.parse(fs.readFileSync('pokes.json', 'utf8'));
async function get_learned_by_pokemon(curr_move, move_dict, current_poke){
response = await axios.get('https://pokeapi.co/api/v2/move/'.concat(curr_move))
let learned_by_pokemons = []
for (let k = 0; k < response.data.learned_by_pokemon.length; k++){
learned_by_pokemons.push(response.data.learned_by_pokemon[k].name)
}
// pass
if (learned_by_pokemons.includes(current_poke)){move_dict[current_poke].pass.push(curr_move)}
// fail
else{move_dict[current_poke].fail.push(curr_move)}
}
function getPokes(){
//iterate through all pokemon
for (let i = 0; i < db_data.length; i++){
let current_poke = db_data[i].name
let moves = db_data[i].moves
move_dict[current_poke] = {pass: [], fail: []}
//iterate through all moves of pokemon
for (let j = 0; j < moves.length; j++){
let curr_move = moves[j]
//get data on current move
get_learned_by_pokemon(curr_move, move_dict, current_poke)
}
}
}
getPokes()
}
I've also used an await before the Axios.get()
. I'd like to know why move_dict is not filling and I'd like to overcome this problem without using a setTimeout()

It looks like the OP begins with a json file describing pokemons. From the code, it looks like the file contains at least the following...
[
{ name: 'nameA', moves: [ 1, 2, 3, ... ] },
{ name: 'nameB', moves: [ 3, 4, 5, ... ] },
...
]
It looks like there's an api endpoint that takes a move ("id or name") and returns, among other things, a list of pokemons that have "learned" that move.
And it looks like the OP aims to produce a dictionary like this...
{
nameA: { pass: [1, 2, ...], fail: [3, 4, ...] },
nameB: { pass: [3, 4, ...], fail: [4, 5, ...] },
...
}
... where the pass and fail arrays moves found in the input file that either are or are not learned moves according to the api.
Since we don't want to call the api redundantly, and since there might be redundant moves in the input file, it 's worthwhile to create an intermediate structure that associates unique moves in the input the pokemons who have learned them, like this...
{ // key is a move id or name, value is an array of pokemon names
1 : [ 'nameA', 'nameB', ... ],
2 : [ 'nameC', 'nameD', ... ],
...
}
So here's how I'd describe the idea in code (not compiled or tested)...
async function get_learned_by_pokemon(move){
const response = await axios.get(`https://pokeapi.co/api/v2/move/${move}`);
return response.data.learned_by_pokemon.map(p => p.name);
}
async function move() {
let db_data = JSON.parse(fs.readFileSync('pokes.json', 'utf8'));
let allMoves = db_data.flat_map(p => p.moves);
let uniqueMoves = [...new Set(allMoves)];
// map move ids to pokemons who learned the moves { moveId: [ 'pokeA', 'pokeB' ...], ...}
let learnedBy = {}
for (let move in uniqueMoves) {
learnedBy[move] = await get_learned_by_pokemon(move);
}
// map pokemon names to a pair of arrays indicating if they have learned their moves (pass) or not (fail)
let move_dict = db_data.reduce((acc, p) => {
let name = p.name;
let pass = p.moves.filter(move => learnedBy[move].includes(name));
let fail = p.moves.filter(move => !learnedBy[move].includes(name));
acc[name] = { pass, fail };
return acc;
}, {});
return move_dict;
}

there are 2 wrong with your code.
1: where do you call getPokes() ?
2: get_learned_by_pokemon is async function so you have to use await if you want to wait for it done

Related

find duplicate values in array of objects in javascript

I have two lists in javascript that are of same structure like below:
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
I need to remove all records from database that are in existing documents list (i.e "dt") but NOT in required_documents list.
For the above scenario I should remove only {"id":3,"dt":4} and insert {"id":3,"dt":3}. I am not sure how I can compare on just one property. This is below that I found on SOF sometime ago but can't find it again apologies for not referencing it.
required_documents.forEach((obj) => {
const elementInArr2 = existing_documents.find((o) => o.dt === obj.dt);
console.log('found elementinarr: ' + obj.dt);
});
This returns unique objects like dt:1,dt:2,dt:3 but I need dt:4 from the existing documents list as it is the one that is not in the required documents list and needs to be deleted. How can I get just the one that is not in the required documents list.
Assuming both id and dt properties are significant, I would first create a means of hashing an entry and then build a hashed set of required_documents.
Then you can filter out anything from existing_documents that is in the set, leaving only the results you want.
const required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
const existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
// a simple stringify hash
const createHash = ({ id, dt }) => JSON.stringify({ id, dt });
const requiredHashSet = new Set(required_documents.map(createHash));
const result = existing_documents.filter(
(doc) => !requiredHashSet.has(createHash(doc))
);
console.log(result);
The hash creation can be anything that produces a comparable entity that can uniquely identify a record.
You need to run it twice to confirm there is no elements left in existing. So create a function and use it.
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}]
let output = [];
output = output.concat(extractUniqueValues(required_documents, output));
output = output.concat(extractUniqueValues(existing_documents, output));
console.log(output)
function extractUniqueValues(input, output){
return input.filter((item)=>{
return !output.find(v => v.dt == item.dt)
})
}
You can do like below
var required_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 3 },
];
var existing_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 4 },
];
for (let index = 0; index < required_documents.length; index++) {
const element = required_documents[index];
for (var i = existing_documents.length - 1; i >= 0; i--) {
const child = existing_documents[i];
if (element.id === child.id && element.dt === child.dt) {
existing_documents.splice(i, 1);
} else {
required_documents.push(element);
}
}
}
LOG not exist [{"dt": 4, "id": 3}]
LOG unique items [{"dt": 1, "id": 1}, {"dt": 2, "id": 2}, {"dt": 3, "id": 3}]
If you don't care about time complexity, something this should work:
var new_documents = existing_documents.filter(ed => {
return required_documents.find(rd => rd.dt == ed.dt);
});
Edit Okay, I just reread your question and I'm a bit confused. Do you want the object {id: 3, dt: 3} inside the new array as well?

Efficient way to delete from an array of objects in JavaScript

frontendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion"},
{"id": 2, "name": "User Creation", "script_name": "UserCreation"}
]
backendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
]
I'm trying to delete the entry with id = 1 in frontendtask and push the entry from backendtask with this code.
if (backendtasks != 0) {
for (updated_task in backendtasks ) {
for (oldtask in frontendtasks) {
if (frontendtasks[oldtask].id == backendtasks[updated_task].id) {
frontendtasks[oldtask] = backendtasks[updated_task]
delete backendtasks[updated_task];
break;
}
}
}
for (new_task in backendtasks) {
frontendtasks.unshift(backendtasks[new_task])
}
}
This is really slow and CPU hits 100% in browser with 700 items. Is there any efficient way to implement this?
Don't loop through both arrays, instead use an object to map backend ids to values:
const mappings = {};
for (const task of backendtasks) {
mappings[task.id] = task;
}
for (let i = 0; i < frontendtasks.length; i ++) {
const curid = frontendtasks[i].id;
if (curid in mappings) {
frontendtasks[i] = mappings[curid];
delete mappings[curid];
}
}
// push is faster than unshift
for (const key in mappings) {
frontendtasks.push(mappings[key]);
}
Approach: Since you have 2 arrays, I would suggest first normalizing backend array to an object, and then iterate on frontend array and lookup in normalized object as lookup in object is O(1) as compared to O(n) in array.
function getFrontendTasks(){
const frontendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion"},
{"id": 2, "name": "User Creation", "script_name": "UserCreation"}
]
const backendtasks = [
{"id": 1, "name": "User Deletion", "script": "UserDeletion_V2"}
]
const normalizedBackendTasks = backendtasks.reduce((acc, val) => ({...acc, [val.id]: val}), {});
const newFrontendTasks = frontendtasks.map((task) => normalizedBackendTasks[task.id] || task);
return newFrontendTasks
}
console.log(getFrontendTasks())
Creating a mapping table reduces the time complexity from O(n^2) to O(n), by removing the nested for loops, which is very expensive.
Try the following code:
const map = {};
backendtasks.forEach(bt => (map[bt.id] = bt));
frontendtasks.forEach((ft, idx) => {
if (map[ft.id]) {
frontendtasks[idx] = map[ft.id];
delete map[ft.id];
}
});
frontendtasks = frontendtasks.concat(Object.values(map));
Somehow I didn't see the map() function in any solution that creates a new array as shown below.
This will return the new array with the new value. As you can see, it takes an array, an id (this could be anything and any type tho), and a callback.
Searcing for the id in the array and runs the callback when found. Efficient way for what you want to do.
In the callback, I used find() on the backendtasks simply because I need to find the item which has the same id (id: 1).
When found, it returns the item from backendtasks then completes the function by returning that value in the map() function.
So, this should be O(n), considering that the callback only runs once and it's a more elegant solution for multiple uses in my opinion.
const frontendtasks: any[] = [];
const backendtasks: any[] = [];
const fn = (arr: any[], id: number, callback: (removed: any) => any) => {
return arr.map((ft) => {
if (ft.id !== id) return ft;
else return callback(ft);
});
};
fn(frontendtasks, 1, (rm) => backendtasks.find((id) => rm.id === id));

oracle node executeMany with dynamic sql query

I am trying to do UPDATE query with executeMany statement. I have tried following with execute in async forEach loop like this:
export const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
then some dynamicDefinitions
export const buildDynamicDefs = (data,enumRef) => {
const defs = {...data};
Object.keys(defs).forEach(key => defs[key] = enums.typeDefs[enumRef][key])
return defs;
};
then finally execute call
...
await asyncForEach(dataArray, async (device) => {
response[device.deviceId] = await connection.execute(
queryPut(device),
{
bind1,
...device,
},
{
autoCommit: false,
bindDefs: {
bind1,
...buildDynamicDefs(device,'enumName'),
},
},
)
})
and query looks following :
export const queryPut = data => `
UPDATE status
SET MANDATORY_PROP1=:bind4
${data.bind2 && ',OPT_PROP1=:bind2' || ''}
${data.bind3 && ',OPT_PROP2=:bind3' || ''}
WHERE MANDATORY_PROP2=:bind1 AND MANDATORY_PROP3=:bind5`;
Problem is that when i use executeMany, i do not know how to tell query how should it looks like
.executeMany(query,arrayOfObject-binds,options)
There is only one query. And my array of objects may looks following:
[
{
bind1: 1,
bind4: 4,
bind5: 5,
},
{
bind1: 1,
bind2: 2,
bind4: 4,
bind5: 5,
},
]
Is there some possibility to optionally add or include that set OPT_PROP1=:bind2 in sql? is there some check for null value of binds?
With execute it is working, but i dont think it is efficient for big numbers of data.
Thanks,
After some investigation all i needed to do it was change SQL a bit:
UPDATE status
SET MANDATORY_PROP1=:bind4,
OPT_PROP1=COALESCE(:bind2,OPT_PROP1),
OPT_PROP2=COALESCE(:bind3,OPT_PROP2),
WHERE MANDATORY_PROP2=:bind1 AND MANDATORY_PROP3=:bind5`;

Having issue with typescript dictionary

I am building an typescirpt dictionary like that:
const skills = x
.map(y => y.skills)
.flat(1)
.map(z => {
return { [z.id]: { skill: z } };
});
That is the array I am getting by the code above:
{ 7ff2c668-0e86-418a-a962-4958262ee337: {skill: {…}} }
{ c6846331-2e11-45d6-ab8d-306c956332fc: {skill: {…}} }
{ 0fc0cb61-f44d-4fd0-afd1-18506380b55e: {skill: {…}} }
{ 36dc0b74-84ee-4be2-a91c-0a91b4576a21: {skill: {…}} }
Now the issue is I can not access the dictionary by key:
const id = '7ff2c668-0e86-418a-a962-4958262ee337';
const one = myArr.find(x => x === id); // returns undefined
const two = myArr[id]; // returns undefined
Any ideas how to fix?
You can use Object.keys() to get the key of each of your objects. In your case the key of each of your objects is its id. Then use that to check whether it equals x (you search id).
See example below:
const myArr = [
{"7ff2c668-0e86-418a-a962-4958262ee337": {skill: 1}},
{"c6846331-2e11-45d6-ab8d-306c956332fc": {skill: 2}},
{"0fc0cb61-f44d-4fd0-afd1-18506380b55e": {skill: 3}},
{"36dc0b74-84ee-4be2-a91c-0a91b4576a21": {skill: 4}}],
id = "36dc0b74-84ee-4be2-a91c-0a91b4576a21",
one = myArr.findIndex(x => Object.keys(x)[0] === id); // the index of the object which has the search id as its key.
myArr[one] = {newKey: "newValue"}; // set the index found to have a new object
console.log(myArr);
You are now creating an array of objects. I suggest you create an object instead, with your ids as keys
Example:
const skills = x
.map(y => y.skills)
.flat(1)
.reduce((acc, z) => {
acc[z.id] = z;
return acc;
}, {});
Your myArr is going to look something like:
{
'7ff2c668-0e86-418a-a962-4958262ee337': {...}
'c6846331-2e11-45d6-ab8d-306c956332fc': {...},
'0fc0cb61-f44d-4fd0-afd1-18506380b55e': {...},
'36dc0b74-84ee-4be2-a91c-0a91b4576a21': {...}
}
You can then access it the way you intended:
const skill = myArr['7ff2c668-0e86-418a-a962-4958262ee337'];
make use of map that can help,
Map is a new data structure introduced in ES6. It allows you store key-value pairs similar to other programming languages e.g. Java, C#.
let map = new Map();
const skills = x
.map(y => y.skills)
.flat(1)
.map(z => {
map.set(z.Id, { skill: z })
return map;
});
//Get entries
amp.get("7ff2c668-0e86-418a-a962-4958262ee337"); //40
//Check entry is present or not
map.has("7ff2c668-0e86-418a-a962-4958262ee337"); //true

Create object from JSON using headers from JSON

I have a JSON object that is similar to below:
vehicles:
{
trucks: [
"Ford",
"Toyota",
"Dodge",
],
suvs: [
"Honda",
"GMC",
],
cars: [
"Pontiac",
"Lotus",
"Aston-Martin",
"Porsche",
"Subaru"
]
}
I would like to loop through this and create my own object, however I cannot find out how to do it without using three different loops for each type of vehicle.
Here is my attempt below:
let vehicleObject = {
vehicles: []
}
// I'm getting the response back from a http request
Object.keys(body.vehicles).forEach(function (k) {
for (let i = 0; i < body.vehicles.k.length; i++) {
vehicleObject.vehicles.push({
vehicle_type: k,
manufacturer: body.vehicles.k[i]
});
}
});
However, this just leads me to "cannot read property length of undefined. I know I can accomplish this with a switch or three if's but I would like to learn a more efficient way, if possible. Thank you.
Loop should be like this:
Object.keys(body.vehicles).forEach(function (k) {
for (let i = 0; i < body.vehicles[k].length; i++) {
vehicleObject.vehicles.push({
vehicle_type: k,
manufacturer: body.vehicles[k][i]
});
console.log(vehicles[k].length)
}
});
When you iterate over each key you are getting name of the keys in k and then to get the array from body.vehicles object you need to do something like body.vehicles[k].
To declare global variable for the scope you should probably use var instead of let
var vehicleObject = {
vehicles: []
};
let is reachable only inside {...} block, like you used it in your for {...} loop.
Looks like you want an array of objects, like:
[{ vehicle_type: 'suvs', manufacturer: 'Honda' }, ... ]
Assuming, body contains the vehicles object:
const { vehicles } = body
const vehicleTypes = Object.keys(vehicles)
const getManufacturers = type => vehicles[type]
const createVehicle = (type, mf) => ({ vehicle_type: type, manufacturer: mf })
let flattenedVehicles = []
vehicleTypes.forEach(type => {
flattenedVehicles.push(
getManufacturers(type).map(mf => createVehicle(type, mf))
)
})
// flattenedVehicles now has desired array

Categories

Resources