Lodash convert array of object to different structure - javascript

How can I transform from the old nested object array to the new flater one.
PLEASE NOTE: userId, quizId, and answerId are different values. They are strings and vary for each object in the array. So there are no issues of duplicates in the below before / after models
Model structure
const old = [{
userId: string,
quiz: [{
quizId: string,
createdAt: string,
completedAt: string,
answers: [{
answerId: string,
value: any,
updatedAt: string
}]
}]
}]
const new = [{
userId: string,
quizId: string,
createdAt: string,
completedAt: string,
responses: {
answerId: {
value: any,
updatedAt: string
}
}
}]
Each object in the array is unique on the composite key of userId quizId. In the old object the record has an array of answers whilst in the new object it has an object of responses the data contained within each is identical (answerId, value, updatedAt`) but the structure is of course different.
Current solution.
const allUniqueUserIds = [...new Set(refactoredQuizs.map(quiz => quiz.userId))]
const usersQuizDto: UsersQuizsDto[] = allUniqueUserIds.map((id) => {
const userQuizs = refactoredQuizs.filter(quiz => quiz.userId===id);
return {
RespondentID: id,
Quizs: userQuizs.map(quiz => {
return {
StartedAt: (quiz.createdAt as any)?.toDate()?.toUTCString(),
CompletedAt: (quiz.completedAt as any)?.toDate()?.toUTCString(),
UpdatedAt: '',
QuizId: quiz.quizId,
Answers: _.toPairs(quiz.responses).map(([answerId, { value, updatedAt }]) => {
return {
AnswerId: answerId,
Value: value,
UpdatedAt: (updatedAt as any)?.toDate()?.toUTCString()
}
})
}
})
}
});

Related

MongoDb - Delete Json object from array

I would like to delete an object from a JSON objects array. Here is the schema
qualifications: {
Experience: [{
title: String,
companyName: String,
location: String,
years: Number
}],
Education:[ {
school: String,
years: Number,
}],
Licences: [String],
Honnors: [String],
}
For example how can I delete the object whose key is "school": "harvard university" ?
What i tried is
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{
$pull: {
qualifications: {
Education: {
school: "harvard university",
}
},
},
}
);
But unfortunatelly it doesn't get deleted from the database. what is wrong?
can you try:
await User.update({ _id: req.body.userid },
{
"$pull": {
"qualifications.Education": {
"school": "harvard university",
},
},
});
qualifications is an object, thus you can't use the $pull operator which requires an array field. Instead, you need the (.) dot notation to update the nested Education array: qualifications.Education.
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{
$pull: {
"qualifications.Education": {
school: "harvard university"
}
}
})
Demo # Mongo Playground
Updated
From your error message, it seems qualifications is an array instead of an object. Your schema should be as below:
qualifications: [{
Experience: [{
title: String,
companyName: String,
location: String,
years: Number
}],
Education:[ {
school: String,
years: Number,
}],
Licences: [String],
Honnors: [String],
}]
To remove the object from the nested arrays, the below query aims to remove all the objects with school: "harvard university" from the Education array, for the all objects in the qualifications array,
const user = await User.findOneAndUpdate(
{
_id: req.body.userid,
"qualifications.Education.school": "harvard university"
},
{
$pull: {
"qualifications.$[].Education": {
school: "harvard university"
}
}
})
Demo (remove object from nested arrays) # Mongo Playground

MongoDB findOne using $and & $elemMatch not working?

I am trying to check if there is an existing conversation between two users before creating another one.
My Conversation object stores the conversation participants in an array, I need to return a conversation object that has BOTH participants (senderId & recId) that exists in my database but I am unable to build to correct MongoDB query to get it.
Please see the queries I have tried below because I have tried all manner of using $and & $elemMatch but can't get it to work.
Thank you
Conversation Model
const conversationSchema = mongoose.Schema(
{
participants: [participantSchema],
},
{timestamps: true}
)
const participantSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: `User`,
},
username: {
type: String,
required: true,
}
}
)
Conversation Object
{
_id: 61cb6316asas4b54e09168234,
participants: [
{
userId: 61b777ea6815a69a625b,
username: 'johnsmith'
},
{
userId: 61bc0dcbe7181ccfd806,
username: 'testuser'
}
],
createdAt: 2021-12-28T19:18:46.673Z,
updatedAt: 2021-12-28T23:41:12.364Z
}
Queries I have tried that ARE NOT what I need or don't work
// null - no convo found when the convo definitely exists in db
const existingConvo = await Conversation.findOne({
$and: [{ userId: senderId }, { userId: recId }],
})
// works but only checks for ONE id property
// if I make an array: "Query filter must be an object, got an array"
const existingConvo = await Conversation.findOne({
participants: { $elemMatch: { userId: senderId } },
})
// "Unknown operator $and"
const existingConvo = await Conversation.find({
participants: {
$and: [{ userId: senderId }],
},
})
// returns empty array when it should have the convo object
const existingConvo = await Conversation.find({
participants: { $all: [{ userId: senderId }, { userId: recId }] },
})

Nodejs, Mongodb filter sub array of array of objects

Hi everyone I have an array of objects with some populated fields. This is the schema of the product.
import mongoose, { Schema } from 'mongoose';
const productSchema = new mongoose.Schema(
{
name: String,
description: String,
sku: String,
barcode: String,
isActive: Boolean,
quantity: Number,
availability: String,
taxClass: [{ type: Schema.Types.ObjectId, ref: 'TaxClass' }],
images: [{ type: Schema.Types.ObjectId, ref: 'Image' }],
variants: [{ type: Schema.Types.ObjectId, ref: 'Variant' }],
tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }],
price: {
comparePrice: Number,
price: Number
},
seo: {
name: String,
keywords: [
{
name: String
}
],
description: String,
image: String
}
},
{ timestamps: true }
);
const Product = mongoose.model('Product', productSchema);
export default Product;
So i have a function and I want to return all the products with the variant color of green.
export const returnFilteredProducts = async (_, { filters = null, page = 1, limit = 20 }, context) => {
await jwtAuthentication.verifyTokenMiddleware(context);
try {
let searchQuery = {};
const products = await Product.find(searchQuery).populate(['variants', 'tags', 'images', 'taxClass']);
console.log(products.filter((item) => item.variants.filter((e) => e.color.indexOf('green') >= 0)));
return {
products
};
} catch (error) {
handleError(error);
}
};
The thing is that it does not return me the document with a variant color of green, instead it returns all the documents.
I am implementing a filtering system so I don't filter the products with in the frontend with redux.
Regarding the filtering method that is applied into the products array:
products.filter((item) => item.variants.filter((e) => e.color.indexOf('green') >= 0))
Inner call item.variants.filter() returns an array.
The outer call: products.filter() will include the product item, since the array will coerce into true, even when empty.
You can use method Array.some() for the inner call,
which will return a boolean true if at least one item (e) in item.variants
has the desired color.
This way you will filter-out all the product items that do-not contain the desired color in at least one element of the item.variants array.

Normalizr: Identifying entities by type rather than schema for polymorphic mappings

For a polymorphic schema such as Union in Normalizr, for schema definitions and data:
const data = { owner: { id: 1, type: 'user', name: 'Anne' } };
const user = new schema.Entity('users');
const group = new schema.Entity('groups');
const unionSchema = new schema.Union({
user: user,
group: group
}, 'type');
const normalizedData = normalize(data, { owner: unionSchema });
normalized data takes the form:
{
entities: {
users: { '1': { id: 1, type: 'user', name: 'Anne' } }
},
result: { owner: { id: 1, schema: 'user' } }
}
The entities are keyed on the schema key, in this case, users, but the result object includes only the key for the schema in the UnionSchema definition. This can make it difficult to match up the elements later without full denormalization.
Is there some better way to normalize such data with normalizr to make it easier to pull the entity from the entities, given the result? For my purposes, ideally, data could be normalized from something like:
const data = { owner: { id: 1, type: 'users', name: 'Anne' } };
to
{
entities: {
users: { '1': { id: 1, type: 'users', name: 'Anne' } }
},
result: { owner: { id: 1, type: 'users' } }
}
Note that the type matches the entity key (that is pretty trivial), and the name of the key in result is type (more of a pain if you want to do it with more complex data). I suspect that that sort of normalization would make it harder to denormalize, but I'm interested in normalization only.
Got an answer on this:
https://github.com/paularmstrong/normalizr/issues/281
Apparently, the behavior is intentional and is not going to change--there is no way to use Normalizr to do what I asked.

Nested Javascript Object : recursion and "complex" transformation (with lodash)

I apologize in advance for the complex example here; I tried to trim it down as much as I could to illustrate what I try to achieve
I have a complex structure that I need to traverse and transform based on some conditions; Here's an (short) example of the structure that should cover most scenarios:
{ PROP1: {
metadata: Object, // somewhere deeper in metadata I have a `value` key
parent: { $ref: String },
employee: {
parent: { $ref: String },
id: String,
metadata: Object,
products: {
metadata: Object,
model: { $ref: String },
type: 'array',
value: ["String", "String" , "String"] ,
[...]
},
model: {
id: String,
email: {
metadata: Object,
value: 'a#b.com',
type: 'string',
validity: Object,
[...]
},
name: {
firstName: {
metadata: Object,
value: 'John',
type: String,
validity: Object,
[...]
},
lastName: {
metadata: Object,
value: 'Smith',
type: String,
validity: Object,
[...]
},
}
},
operations: {
id: String,
items: [
{ action: {value: "UPDATE", model: {$ref: String }, [...] },
{...}
],
metadata: Object,
[...]
}
}
},
PROP2: {
// similar as PROP1
},
[... and so on ...]
}
I basically need to clean that up before sending it to the backend;
Whenever a value contains $ref, I don't want the key/val pair (e.g.: PROP1.parent is of no use and can be omitted)
whenever a value contains value, I need to omit everything else and move the value of value as the value of key (e.g.: PROP1.employee.products should equal ['String', 'String', 'String'])
keys like id, metadata, validity (etc) can be completely omitted regardless of its content
So the end result should be along those lines:
{ PROP1: {
employee: {
products: ['item','item','item'],
model: {
email: 'a#b.com',
name: { firstName: 'John', lastName: 'Smith'},
},
operations: [
{action: 'UPDATE'}
]
}
},
PROP2: { ... }
}
I tried lots of different approaches using different lodash methods but couldn't wrap my head around this...
Any help will be greatly appreciated
Thanks
In pseudo code, try something like this. Implement the specifics and post more info when you run into trouble.
var ignoreKeyArray = ["id", ...] // keys to ignore and remove
var newJSON = "{}";
for (property in JSON) {
var newProp = parseJSON(key, property);
insert newProp in newJSON object
}
var parseJSON = function (key, jsonBlob) {
if (key in ignoreKeyArray || jsonBlob contains value "$ref")
return "";
var jsonOut = key + ": {}";
for (child in jsonBlob) {
add parseJSON(child) to jsonOut;
}
return jsonOut;
}
If you have any questions, comment so I can extend the answer and clarify.

Categories

Resources