Javascript: Array of objects, changing value in each object asynchronously - javascript

So, I know this has been asked before and I have tried other answers like .map, (function(post){ async })(value), and I am still stuck...
so, I have an array of objects and a for loop:
var postsData = [{thumbnail: www.website.com/image.jpg}, {thumbnail: www.website.com/image.jpg}, {thumbnail... etc}];
for (let i = 0; i < 3; i++) {
let thumbnail = postsData[i].thumbnail;
Cloudinary.uploader.upload(thumbnail, function(result){
// not sure what to do here
// result comes back as an object, and I want to change each thumbnail in
// the postsData array to be result.public_id
}, {transformation:[{}]});
} // go through all the items in the array
// do something with "updated" postsData array
An example would really help, as obviously, getting the values changed involves some async functions.

Set "thumbnail" property of object in array to result.public_id. Create a function where expected parameter is current object within postsData array, set "thumbnail" property of object by passing function reference to upload function, passing current object of array prop object utilizing Function.prototype.bind()
var len = postsData.length;
var n = 0;
function handleData(result, prop) {
prop["thumbnail"] = result_public.id;
if (++n === len) complete(postsData);
}
function complete(data) {
console.log(data, postsData);
}
for (let prop of postsData) {
Cloudinary.uploader.upload(
thumbnail
, handleData.bind(null, prop)
, {transformation:[{}]}
);
}
plnkr http://plnkr.co/edit/SSyUG03pyAwXMVpHdGnc?p=preview

From what I understand, you are trying to loop through an array and do async function on each of its element. What I would do is to use Promise.js (see enter link description here). So the code would look something like this:
// create an array for the promises
const PromiseArr = [];
postsData.map(d => {
PromiseArr.push(
// do async actions and push them to array
new Promise((resolve, reject) => {
Cloudinary.uploader.upload(thumbnail,(result) => {
// return the result
resolve(result);
});
})
);
})
// do all async actions and store the result in result array
const result = PromiseArr.all();

Related

Insert element inside array

I have a function
checkName(output) {
output.filter((NewData) => {
return this.props.elements.filter((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name,
//there i need to add another element
// Need to add newData.number = OldData.number
}
return NewData
})
})
return output
}
and I call this function like:
const named = this.checkName(product.rows)
Now I need to add to my product's array that I passed to checkName the value "OldData.Number" to "newData.Number" that is not defined in product (so I need to create this field)
For example:
Product before the checkName function
product.rows = [NewData.name]
Product after the checkName function
product.rows = [NewData.name="value of OldData.name", NewData.number="value of OldData.number"]
How can I obtain this result?
There are 2 confusing things in your code:
You are using filter to execute an action in each member of the output array. However, filter should be used to... well, filter that array, meaning that is should not modify it, just return a sub-set of it. Instead, you might want to use forEach. However, taking into accound the next bullet, probably you want to use map.
You are modifying the array passed to the checkName function. This is confusing and can lead to hard-to-find bugs. Instead, make your function "pure", meaning that it should not mutate its inputs, instead just return the data you need from it.
I would suggest some implementation like this one:
checkName(output){
return output.map((NewData) => {
// find the old data item corresponding to the current NewData
const OldData = this.props.elements.find(x => x.key === NewData.key);
if (OldData) {
// If found, return a clone of the new data with the old data name
// This uses the spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
return {
...NewData, // Clone the NewData object
name: OldData.name, // set the value found in OldData.name in the "name" field of the cloned object
number: OldData.number, // You can do the same for each field for which you want to replace the value cloned from NewValue
};
} else {
// Otherwise, just return a clone of the NewData
return { ...NewData };
}
}
}
The usage would be like this:
const named = this.checkName(product.rows)
Be aware that the product.rows array won't be modified!
You can get keys and values of the old object.
const keys = Object.keys(oldObject);
const values = Object.values(oldObject);
// or
const [keys, values] = Object.entries(oldObject);
After, you will create a loop with all keys of oldObject, and insert in newObject like a array.
keys.forEach( (key, index) => newObject[key] = values[index]);
// or
for (const [key, value] of Object.entries(object1)) {
newObject[key] = value
}
Use map like this.
checkName(output){
return output.map(( NewData) =>{
this.props.elements.forEach((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name;
NewData.number = OldData.number;
}
})
return NewData;
})
// return output;
}

Set arguments dynamically with Promise.all().then()

The code below works for me
Promise.all([first, second, third]).then([first, second, third] => {
console.log(second);
});
I know that console.log(second) will give me the value with the key second.
My promises are dynamically set and now it looks like below:
let collection = [second, third];
Promise.all(collection).then((collection) => {
console.log(collection);
});
In this example I set two values in collection. In real life it can include more or less values.
When I use console.log(collection) it will output collection[0] and collection[1]. In this case I don't know what which value collection[1] is.
Question
How can I, like my first example, have something like named dynamically arguments like collection['second'] or similar?
As we want to access the value dynamically, set collection to an empty object first. Then, use the keys from collection to pass all its Promise-values to Promise.all. Then, map back the fulfilled values and then, we can access collection's value by some key.
let collection = {}
for (let i = 0; i < 3; i++) {
collection[`key${i}`] = Promise.resolve(i)
}
let collectionKeys = Object.keys(collection)
Promise.all(collectionKeys.map(key => collection[key]))
.then(values => {
let collectionFulfilled = collectionKeys.reduce((obj, key, i) => {
obj[key] = values[i]
return obj
}, {})
console.log(collectionFulfilled)
})
If you pass your promises embedded inside an object with a single key, you could use that for it's name, and then with a simple helper function reverse the values & keys from this.
With the new ES6 you can then just pass like -> [{one}, {two}, {three}] etc.
Below is an example with a helper function called namedPromiseAll.
function namedPromiseAll(named) {
const pcollection =
named.map(m => Object.values(m)[0]);
const ncollection =
named.map(m => Object.keys(m)[0]);
return Promise.all(pcollection).then((c) => {
return c.reduce((a,v,ix) => {
a[ncollection[ix]] = v;
return a;
}, {});
});
}
const second = Promise.resolve(2);
const third = Promise.resolve(3);
const collection = [{second}, {third}];
namedPromiseAll(collection).then(console.log);

js map why is not changing my value with return

I'm doing map with a variable, and doing a return to change his value, but it's not working, the value for the whole array is the same:
//resultValues = processValues(table,resultValues,'toClient');
resultValues.map ( (record) => {
record = processValues(table,record,'toClient');
return record;
});
return Promise.resolve(resultValues); // does not change
so I had to create another variable to be able to have a change on the array. why is this behavoiur? it's normal in map ?; is there another option with lodash by example to don't need create a second variable?
let newResult = [];
resultValues.map ( (record) => {
record = processValues(table,record,'toClient');
newResult.push(record); // with this changes are sent to new var
//return record;
});
// return Promise.resolve(resultValues);
return Promise.resolve(newResult);
Array.map returns a new array instance where each element inside it is transformed:
let ret = resultValues.map (record => processValues(table,record,'toClient'));
return Promise.resolve(ret);
Map returns a new array. You could literally do:
const newArr = resultValues.map ( (record) => {
record = processValues(table,record,'toClient');
return record;
});
Please read the first sentence of the MDN web docs for map().

How to call a object method without object instance?

I have a method loadSet which creates elements with datas from the localstorage, and this should be run on page load i am calling it via
ReminderSet.prototype.loadSet(); // works fine
My question is, is there any other way to call a method that don't need a reference to an object instance? like person1.loadSet(); or should i abandon this and make it as a regular function?
ReminderSet.prototype.loadSet = function() {
var keys = Object.keys(localStorage),
i = 0,
key,
array;
for (; key = keys[i]; i++) {
const setId = localStorage.getItem(key);
array = JSON.parse(setId); //parse and store key values
let array_index = 0;
//Re-create the reminders and set their properties//
$reminderSection.append($('<div/>').addClass('set').attr('id', key) //Set the ID
.append($('<div/>').addClass('set-title').append($('<h1>').attr('contenteditable', 'true').text(array[array_index].set_title)), //Index is always at 0//
$('<div/>').addClass('create-reminder-control').append($('<button>').addClass('add-new-reminder').text("+ add new"), $('<input>').addClass('create-reminder-value').attr({ type: "text", placeholder: "get something done" })), $('<div/>').addClass('reminder-lists'), $('<div/>').addClass('save-control').append($('<button>').addClass('save-reminder-button').text('Save'))))
//Get our key values //
for (; array_index < array.length; array_index++) {
/*Select the element id */
$("#" + key).children('.reminder-lists').append($('<div/>').addClass('a-reminder').attr('contenteditable', 'true').text(array[array_index].description).append($('<div/>').addClass('delete-reminder').text('x'))) //Get the reminders
} //end
}
};
If loadSet doesn't need or use an instance, it doesn't make any sense for it to be on ReminderSet.prototype. Either make it a standalone function:
function loadSet() {
// ...
}
// Call it like so: loadSet();
...or a property on ReminderSet itself:
ReminderSet.loadSet = function() {
// ...
};
// Call it like so: ReminderSet.loadSet();
Only put functions on the object that a constructor's prototype property refers to if they need to use this (the instance).
You can set the function directly as a property of the other ReminderSet:
ReminderSet.loadSet = function() {//etc.}
Then you can simply call: ReminderSet.loadSet()

Editing a jagged array in Javascript

I wish to simulate a taskbar (of running tasks/apps). I plan to store tasks something like this:
(function ()
{
var tasks = [];
addTask = function (taskName, taskWindow)
{
if (!tasks[taskName]) { tasks[taskName] = []; }
tasks[taskName].push({ taskWindow: taskWindow, taskName: taskName});
};
removeTask = function (taskName, taskWindow)
{
if (tasks[taskName])
{
//Somehow remove the object from the array
}
};
}());
How should I write removeTask() to remove the correct element from this jagged array?
I suggest using object to store your tasks, because it will make your ( specific to your requirement, I am not talking about Array vs Object) code cleaner and easier to maintain
var taskManager = (function(){
function taskManager(tasks){
// Do your tasks validation before passing to this.
var this.tasks = tasks || {}; // tasks value is not private here
}
// Assuming taskID would be unique value
taskManager.prototype.addTask = function (taskName, taskID){
if ( !this.tasks[taskID] ) {
this.tasks[taskID] = { taskID: taskID, taskName: taskName };
}
};
taskManager.prototype.removeTask = function (taskName, taskID){
if (this.tasks[taskID]){
delete this.tasks[taskID];
}
};
return taskManager;
})();
Usage:
var taskManager1 = new taskManager();
taskManager1.addTask(a,b);
taskManager1.removeTask(a);
Arrays are meant to have numeric indexes and you can use .splice() to remove a numeric indexed item from an array. Non-numeric indexes aren't really in the array, they end up just being properties on the array object and they can be removed with the delete operator.
If you don't have numeric indexes, then you should be using an object and use a property to index each item. When doing it that way, you can use delete tasks[taskName] to remove a property from the object.

Categories

Resources