Javascript object not appending new attribute - javascript

I have this function, where I retrieve an array of objects, I then have a for loop to loop through the objects, and append an index or ind attribute to it:
module.exports.getCustomers = async (req, res) => {
let customers = await Customers.find({}, { "_id": 1 });
for (var i = 0; i < customers.length; i++) {
customers[i].ind = i;
}
console.log(customers)
}
but when I run this, the data is returned as
[
{ _id: ... },
{ _id: ... },
...
]
instead of:
[
{_id: ..., ind: 0},
{_id: ..., ind: 1},
...
]
Please how do I fix this

change your for and turn it into a map
module.exports.getCustomers = async (req, res) => {
let customers = await Customers.find({}, { "_id": 1 });
let mappedCustomers = customers.map((customer, index) => {
customer['ind'] = index;
return customer;
});
console.log(mappedCustomers);
return mappedCustomers;
}
or instead returning the customer, you can create a completly new customer.
let mappedCustomers = customers.map((customer, index) => {
return {...customer, ind: index};
});

It looks like your objects are freezed, idk what library you are using to fetch those items from your data source, but you can read about object freezing here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

Try copying the values over to the individual target objects with Object.assign.
module.exports.getCustomers = async(req, res) => {
let customers = await Customers.find({}, {
"_id": 1
});
for (var i = 0; i < customers.length; i++) {
Object.assign(customers[i], {
ind: i
});
}
console.log(customers);
}

I finally solved it. I think mongoose was messing with it. But adding ._doc seems to have fixed it
for (let i = 0; i < customers.length; i++) {
let customer = customers[i],
customer._doc = {
...customer._doc,
index: i
};
}

Related

Setting Array in ReactJS w/ Hooks

I currently have an array of FooBar objects
interface FooBar {
foo: string,
bar: string,
correct: string,
other: string[]
}
const [arrOfObj, setArrOfObj] = useState<FooBar[]>([
{ "foo": "foolicious ", "bar": "barlicious", "correct": "foobarlicious", "other": ["oof", "f00", "rab", "r#b"]},
]);
const [shuffledArrOfObj, setShuffledArrOfObj] = useState<FooBar[]>([
{ "foo": "", "bar": "", "correct": "", "other": [""]},
]);
However I want to randomly shuffle the "other" array in each object so I have a shuffle function:
useEffect(() => {
(async () => {
const act = await shuffleObjs();
setShuffledArrOfObj([...act]);
})();
}, [arrOfObj]);
const shuffleObjs = () => {
let holdArr = [...arrOfObj];
for (let i: number = 0; i < holdArr.length; i++) {
holdArr[i].other = await handleShuffle(holdArr[i].other);
}
}
const handleShuffle = async (array: string[]) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
However, if I console.log(holdArr) at the end of the "shuffleObjs" function, the array of objects is totally different than the array of objects stored in the shuffledArryObj state.
Am I setting the array state the wrong way, or is there something I am missing?
EDIT: Typo wasn't the problem, it's still not working correctly. I also tried adding arrOfObj to the dep array.
You're missing two things:
useEffect(() => {
(async () => {
const act = await shuffleObjs();
setShuffledArrOfObj([...act]);
})();
}, [arrOfObj]); //<-- Add arrOfObj to dependency Array
const shuffleObjs = () => {
let holdArr = [...arrOfObj];
for (let i: number = 0; i < holdArr.length; i++) {
holdArr[i].other = await handleShuffle(hold[i].other); //<-- change hold[i] to holdArr[i]
}
}

Vue returns Observer in promise.then()

I want to fire a vuex action in created() and when I receive a data, then fire new asynchronous method that fetches more data from server. When data is available I will use them in a component. Unfortunatelly I got stuck with Observer returned from Promise. I tried to change data to computed() without luck. I tried to await but it did not help either. The other computed property item works fine. I know that the Observer is Vue's way for reactivity but I do not know how to fix it.
<SeriesBarChart v-if="! inProgress" :series="series" /> // initial attempt
<SeriesBarChart v-if="! inProgress" :series="groups" /> // computed property attempt
data: () => ({
series: [{}, {}],
inProgress: true,
}),
created() {
this.$store.dispatch('GET_POLL', { slug: this.slug }).then(() => {
this.runQueries(this.item._id, ['vehicles=car&vehicles=bike', 'region=PRG']); // await here did not help
});
},
computed: {
item() {
return this.$store.getters.POLL;
},
groups() {
return this.series;
},
},
methods: {
async runQueries(id, queries) {
this.inProgress = true;
const promises = [];
for (let i = 0; i < queries.length; i += 1) {
promises.push(this.$store.dispatch('GET_POLL_VOTES', { id, query: queries[i] }));
}
Promise.all(promises).then((values) => {
for (let i = 0; i < values.length; i += 1) {
this.series[i] = values[i].data.data;
}
});
this.inProgress = false;
}
Because Yom has not posted an answer and he even deleted his helpful comment, I will post my answer for future googlers. The reason why Vue provided the Observer object was a statement this.inProgress = false; outside of the then block. Following code works as expected:
async runQueries(id, queries) {
this.inProgress = true;
const promises = [];
for (let i = 0; i < queries.length; i += 1) {
promises.push(this.$store.dispatch('GET_POLL_VOTES', { id, query: queries[i] }));
}
Promise.all(promises).then((values) => {
for (let i = 0; i < values.length; i += 1) {
this.series[i] = values[i].data.data;
}
this.inProgress = false;
});
}

Adding properties to an object from MongoDB in an array and sending it in the response

I am trying to add the quoteValue key-value in an element (an object in this case) of the users array using the code below.
When I print out console.log(users[0]), it's not showing the value of quoteValue for users[0]. However, console.log(users[0].quoteValue) prints the actual value of quoteValue.
I don't understand how it is possible. It would really appreciate your help!
export async function get_client_users(req, res) {
try {
let users = await User.find({ role: { $eq: 'client' }, status: { $ne: 'deleted' } }, { name: 1, mobile: 1, email: 1, status: 1, ref_id : 1, _id: 1 });
for(let i = 0; i < users.length; i += 1) {
let quotes = await Quote.find({client: users[i]._id});
const totalQuote = quotes.length;
let cost = 0;
for(let i = 0; i < quotes.length; i += 1) {
cost += quotes[i].total_cost;
}
const result = {
totalQuote: totalQuote,
quoteValue: cost
}
Object.assign(users[i], result);
}
return res.status(200).json(users);
} catch(e) {
console.log(e);
return res.status(400).json({ message: 'Technical Error. Please try again later.' });
};
};
I would recommend using destructing (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) if you can to create a new users object (called updatedUsers in the code below) like so:
export async function get_client_users(req, res) {
try {
let users = await User.find({ role: { $eq: 'client' }, status: { $ne: 'deleted' } }, { name: 1, mobile: 1, email: 1, status: 1, ref_id : 1, _id: 1 });
let updatedUsers = [];
for(let i = 0; i < users.length; i++) {
let quotes = await Quote.find({client: users[i]._id});
let quoteValue = 0;
for(let i = 0; i < quotes.length; i++) {
quoteValue += quotes[i].total_cost;
}
updatedUser = {
...users[i],
totalQuote: quotes.length,
quoteValue
}
updatedUsers.push(updatedUser);
}
return res.status(200).json(updatedUsers);
} catch(e) {
console.log(e);
return res.status(500).json({ message: 'An error occurred. Please try again later.' });
};
};
I also changed a few things like sending 500 instead of 400 when an error occurs, removed the assignment to the totalQuote variable by assigning quotes.length directly to updatedUser.totalQuote, and also used i++ instead of i += 1 in your for loops. I would recommend the usage of a linter such as ESLint (https://eslint.org/) or Prettier (https://prettier.io/) to improve the readability of your code.
Additionally, I would suggest to use map (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) to iterate over your users object and reduce (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) to get the value of quoteValue from the total_cost property of your quotes, but this is outside the scope of your question.

Is there a way to run for loop in a mongoose schema method?

I want to run the following for loop for the length of the array but all it does is to add one by one.
Any idea?
EndorsedSkillSchema.methods = {
async userEndorsedSkill(arr) {
for (var i = 0; i < arr.length; i++) {
const skill = await Skill.findOne({ _id: arr[i]._id });
const s = skill.toJSON();
this.skills.push(arr[i]._id);
await this.save();
pubsub.publish(SKILL_ENDORSED, { [SKILL_ENDORSED]: { ...s } });
return {
endorsed: true,
...s
};
}
}
};
You could use find() and {$in: ids}
const arr = [{_id:1},{_id:2},{_id:3}];
const ids = arr.map(e=>e._id);
Skill.find({_id: {$in: ids}}, function(err, result){
//callback here maybe .insertMany()
});

How to query a model with different attributes

I'm building an app in Express and using Postgres for my database, Sequelize for ORM.
In my database each Post can have one of the 5 types, 1, 2, 3, 4, 5.
I want to show the amount of all the posts by type.
router.route('/post).get(async (req, res) => {
const postOne = await Post.findAll({
where: {
state: 1
}
});
const postTwo = await Post.findAll({
where: {
state: 2
}
});
res.send({ postOne: postOne.length, postTwo: postTwo.length });
I can write like this for all of the 5 types, but I was wondering if there was any shorter way to do it so that I don't have to write the same code 5 times.
Thanks!
const getPostWithType = (type) => Post.findAll({
where: {
state: type
}
});
Now you can await getPostWithType(1) instead. Or even better:
const postTypes = [1,2,3,4,5];
const allPosts = await Promise.all(postTypes.map(getPostWithType));
// allPosts is now an array with the results. You can map it again to get the lengths
const allPostLengths = allPosts.map(p => p.length);
what about using an array? Like below...
let promiseArray = [];
for (let i = 1; i <= 5; i++) {
let promise = Post.findAll({
where: {
state: i
}
});
promiseArray.push(promise);
}
Promise.all(promiseArray).then(function(response) {
//Do whatever...
});

Categories

Resources