json-rules-engine processing array of objects - javascript

Any help would be appreciated !! I couldn't find an answer.
Given input facts to the engine such as -
const facts = {
cart: {
prop1: true,
prop2: "My Cart",
prop3: {
prop4: "North America"
},
products: [
{
category: 1,
classification: null
},
{
category: 2,
classification: null
},
{
category: 1,
classification: null
}
]
}
};
I want to be able to process each object in the products array and define a separate rule for each category and set the classification field for that category.
Something like -
let condition1 = {
all: [
{
fact: 'category',
operator: 'equal',
value: 1
}
]
};
let condition2 = {
all: [
{
fact: 'category',
operator: 'equal',
value: 2
}
]
};
let event1 = {
type: 'category 1 event'
// Update classification
};
let event2 = {
type: 'category 2 event'
// Update classification
};
How do I do this? Do i set the classification field in the event handler for that event? How do I access the object the category belongs to?
I tried using the below, but it selects all the category values from each object. I want to be able to process each object separately.
let condition = {
all: [
{
fact: 'cart',
operator: 'contains',
value: 1,
path: '$.products[*].category'
}
]
}
I cant add them as runTime facts either since you can have only 1 runtime fact and its value. (No Duplicates)

Related

Edit multiple objects in array using mongoose (MongoDB)

So I tried several ways, but I can't, I can modify several objects with the same key but I can't modify any with different keys, if anyone can help me is quite a complex problem
{
id: 123,
"infos": [
{ name: 'Joe', value: 'Disabled', id: 0 },
{ name: 'Adam', value: 'Enabled', id: 0 }
]
};
In my database I have a collection with an array and several objects inside which gives this.
I want to modify these objects, filter by their name and modify the value.
To give you a better example, my site returns me an object with the new data, and I want to modify the database object with the new object, without clearing the array, the name key never changes.
const object = [
{ name: 'Joe', value: 'Hey', id: 1 },
{ name: 'Adam', value: 'None', id: 1 }
];
for(const obj in object) {
Schema.findOneAndUpdate({ id: 123 }, {
$set: {
[`infos.${obj}.value`]: "Test"
}
})
}
This code works but it is not optimized, it makes several requests, I would like to do everything in one request, and also it doesn't update the id, only the value.
If anyone can help me that would be great, I've looked everywhere and can't find anything
My schema structure
new Schema({
id: { "type": String, "required": true, "unique": true },
infos: []
})
I use the $addToSet method to insert objects into the infos array
Try This :
db.collection.update({
id: 123,
},
{
$set: {
"infos.$[x].value": "Value",
"infos.$[x].name": "User"
}
},
{
arrayFilters: [
{
"x.id": {
$in: [
1
]
}
},
],
multi: true
})
The all positional $[] operator acts as a placeholder for all elements in the array field.
In $in you can use dynamic array of id.
Ex :
const ids = [1,2,..n]
db.collection.update(
//Same code as it is...
{
arrayFilters: [
{
"x.id": {
$in: ids
}
},
],
multi: true
})
MongoPlayGround Link : https://mongoplayground.net/p/Tuz831lkPqk
Maybe you look for something like this:
db.collection.update({},
{
$set: {
"infos.$[x].value": "test1",
"infos.$[x].id": 10,
"infos.$[y].value": "test2",
"infos.$[y].id": 20
}
},
{
arrayFilters: [
{
"x.name": "Adam"
},
{
"y.name": "Joe"
}
],
multi: true
})
Explained:
You define arrayFilters for all names in objects you have and update the values & id in all documents ...
playground

Mongoose - renaming object key within array

I have this one schema
{
_id: "123456",
id: "123",
inventory: [
{
id: "foo",
count: 0
},
{
id: "bar",
count: 3
}
]
}
I wanted every "count" keys in the inventory array to be "price" which will look like this at the end:
{
_id: "123456",
id: "123",
inventory: [
{
id: "foo",
price: 0
},
{
id: "bar",
price: 3
}
]
}
And I've tried this
Model.updateOne({ id: "123" }, { $unset: { inventory: [{ count: 1 }] } } )
But it seems to be deleting the "inventory" field itself
The first thing here is to try to use $rename but how the docs explain:
$rename does not work if these fields are in array elements.
So is necessary to look for another method. So you can use this update with aggregation query:
This query uses mainly $map, $arrayToObject and $objectToArray. The trick here is:
Create a new field called inventory (overwrite existing one)
Iterate over every value of the array with $map, and then for each object in the array use $objectToArray to create an array and also iterate over that second array using again $map.
Into this second iteration create fields k and v. Field v will be the same (you don't want to change the value, only the key). And for field k you have to change only the one whose match with your condition, i.e. only change from count to price. If this condition is not matched then the key remain.
db.collection.update({},
[
{
$set: {
inventory: {
$map: {
input: "$inventory",
in: {
$arrayToObject: {
$map: {
input: {$objectToArray: "$$this"},
in: {
k: {
$cond: [
{
$eq: ["$$this.k","count"]
},
"price",
"$$this.k"
]
},
v: "$$this.v"
}
}
}
}
}
}
}
}
])
Example here

How to filter an array of objects based on another array of object's property value? [duplicate]

This question already has answers here:
Filter array of objects with another array of objects
(11 answers)
Closed 4 months ago.
I have two sample arrays below.
let arr1 = [
{ key: "WIHUGDYVWJ", className: "science" },
{ key: "qwljdhkuqwdnqwdk", className: "english" },
{ key: "likubqwd", className: "robotics" }
];
let arr2 = [
{ key: "WIHUGDYVWJ", title: "math" },
{ key: "qwljdhkuqwdnqwdk", title: "english" },
{ key: "likubqwd", title: "robotics" }
];
How can I filter arr1 to get only items that have 'className' value that matches arr2's item's 'title' value? (expecting only items with 'english' and 'robotics' to remain)
How can I filter arr1 to get only items that have 'className' value that do not match arr2's item's 'title' value? (expecting only items with 'science' to remain)
Thanks!
let arr1 = [
{ key: "WIHUGDYVWJ", className: "science" },
{ key: "qwljdhkuqwdnqwdk", className: "english" },
{ key: "likubqwd", className: "robotics" },
{ key: "abcd", className: "history" }
];
let arr2 = [
{ key: "WIHUGDYVWJ", title: "math" },
{ key: "qwljdhkuqwdnqwdk", title: "english" },
{ key: "likubqwd", title: "robotics" }
];
// q1 answer
console.log(arr1.map(arr1Item => arr2.filter(arr2Item => arr1Item.className === arr2Item.title)).flat());
// q2 answer
console.log(arr1.filter(arr1Item => !arr2.some(arr2Item => arr2Item.title === arr1Item.className)));
i wrote without using a for, if/else statement as much as possible.
this code may not be the best. i think it could be more improved.
I hope my answer is helpful

Iterate array of objects by dimension, based on given limit

I have a multi-dimensional array of objects which contain suggested users.
Again each suggested user has some suggested users and so on.
In the application I'm building I select a user (let's call him 'Steve') and would like to receive a certain amount of users connected to that user (by fetching the suggestions). The data is all there, I just need to find a meaningful way to iterate this.
To simplify I have built an array with some sample data where each user has two suggested users:
// Suggested users of my initially selected user (Steve)
let suggested_users_based_on_steve = [
{
name: 'A',
suggested: [
{
name: 'A1',
suggested: [
{ name: 'A1A1' },
{ name: 'A1A2' }
]
},
{
name: 'A2',
suggested: [
{ name: 'A2A1' },
{ name: 'A2A2' }
]
}
]
},
{
name: 'B',
suggested: [
{
name: 'B1',
suggested: [
{ name: 'B1B1' },
{ name: 'B1B2' }
]
},
{
name: 'B2',
suggested: [
{ name: 'B2B1' },
{ name: 'B2B2' }
]
}
]
}
];
Let's say for example I need to get 7 suggested users based on my user Steve.
That means I would iterate the first dimension and get the first two users (A and B).
Since now I have fetched only 2 of 7 users, I need to go one dimension deeper and get the suggested users of A and B which are A1, A2, B1 and B2. This would add up to 6 out of 7 required users, so I need one more.
Therefore I also fetch one suggested user of A1, which is A1A1. Now I need to break / stop the iteration because I'm done.
Please note that the amount of suggested users is not fixed. Every user can have an unlimited amount of suggested users. Also, it's important iterate from layer to layer to get the 'best' and most connected results / users. The initial user is more connected to A and B than to A1A1 and A1A2.
I'm struggling with finding the right combination of nested for...of loops. Is there any recommended way on doing this?
Thank you in advance!
let suggested_users_based_on_steve = [
{
name: 'A',
suggested: [
{
name: 'A1',
suggested: [
{ name: 'A1A1' },
{ name: 'A1A2' }
]
},
{
name: 'A2',
suggested: [
{ name: 'A2A1' },
{ name: 'A2A2' }
]
}
]
},
{
name: 'B',
suggested: [
{
name: 'B1',
suggested: [
{ name: 'B1B1' },
{ name: 'B1B2' }
]
},
{
name: 'B2',
suggested: [
{ name: 'B2B1' },
{ name: 'B2B2' }
]
}
]
}
];
function test(arr, result = []) {
for (let user of arr.values()) {
result.push(user);
if (result.length === 7) return result;
}
var next = arr.reduce(function (all, user) {
return all.concat(user.suggested || []);
}, []);
if (next.length === 0) return result;
return test(next, result);
}
test(suggested_users_based_on_steve);

Array.forEach() and Array.slice() together doesn't work correctly [duplicate]

This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 6 years ago.
it's supposed to Array.slice() let me to make a copy of an array, and then I can modify that copy without modifying the original array, but when I use Array.forEach() over the copy for delete some values this values also are removed from the original array. Does anybody have an idea why this happens?
Here is the code that I've used:
var originalArray = [
{ id: 1, name: 'Sales', datasources: [
{ id:1 , name: 'datasource1', fields: [] },
{ id:2 , name: 'datasource2', fields: [] },
] },
{ id: 4, name: 'Accounts', datasources: [
{ id:3 , name: 'datasource3', fields: [] },
{ id:4 , name: 'datasource4', fields: [] },
] },
{ id: 123, name: 'my datasources', datasources: [
{ id:1 , name: 'datasource1', fields: [] },
{ id:2 , name: 'datasource2', fields: [] },
{ id:3 , name: 'datasource3', fields: [] },
{ id:4 , name: 'datasource4', fields: [] },
] },
{ id: 12, name: 'shared datasources', datasources: [
{ id:13 , name: 'myshared datasource', fields: [] },
{ id:16 , name: 'hello test', fields: [] },
] },
];
var copyOfOriginalArray = originalArray.slice();
copyOfOriginalArray.forEach((folder, index) => {
folder.datasources = folder.datasources.filter((o) => { return o.name.trim().toLowerCase().includes('hello'); });
});
JSON.stringify(originalArray);
JSON.stringify(copyOfOriginalArray);
Acording to this definition.
The slice() method returns a shallow copy of a portion of an array into a new array object.
Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.
for deep copying any object in javascript you can use this function:
function deepCopy(oldObj) {
var newObj = oldObj;
if (oldObj && typeof oldObj === 'object') {
newObj = Object.prototype.toString.call(oldObj) === "[object Array]" ? [] : {};
for (var i in oldObj) {
newObj[i] = deepCopy(oldObj[i]);
}
}
return newObj;
}
Slice will create a copy of the array itself, but it will not clone the objects in the array (those will still be references).
You will need to recursively clone your array and its contents or use something like Lodash's cloneDeep

Categories

Resources