I'm getting a JSON structure from an API, that I want to change in my front end. The frontend adds a property to the JSON structure, "isHidden". When I send the modified JSON back, I don't want the object that has "isHidden" to be sent back to the API, but I will still save that internally in my own mongodb.
However, doing this was aperently a bit harder then I thought. I made this function wich works but I think is very ugly:
function removeHiddenObject(data,parent){
for(var property in data){
if(data.hasOwnProperty(property)){
if(property == "isHidden" && data[property] === true){
parent.splice(parent.indexOf(data), 1);
}
else {
if(typeof data[property] === "object") {
removeHiddenObject(data[property], data);
}
}
}
}
return data;
}
It's a recursive method, but I find it way to complex and weird. Is there a way of simplifying my task?
Here is a jsfiddle for you if you'd like to help out: https://jsfiddle.net/vn4vbne8/
use this code to remove it from json string :
myJson=s.replace(/,*\s*"[^"]"\s*\:\s*{(.*?)"isHidden"\:([^}]*)}/gm,"");
Be careful in Regex every character is important so use exactly above code.
It removes every property that have an object that one of its properties is isHidden.
Javascript actually supports non-enumerable public properties. I'm assuming that when you send data back to the server you first stringify it with JSON.stringify which will only stringify public enumerable properties of the object.
You can define a non-enumerable property like this (more on this here):
Object.defineProperty(obj, 'isHidden', {
enumerable: false,
writable: true
});
Where obj is the javascript object you want to add the property to and isHidden is the name of the property you are adding. When done this way, the new property is accessible as obj.isHidden but nevertheless will not show up in JSON.stringify output nor in for loops.
Here is a solution using object-scan. Takes a bit of time to wrap your head around it, but it helps with some heavy lifting around general data processing.
// const objectScan = require('object-scan');
const data = {"user":{"name":"Bob"},"buildingBlockTree":[{"uid":"a875ed6cf4052448f9e0cc9ff092a76d4","_id":"56a7353f682e1cc615f4a6ae","columns":[{"uid":"a0061fdd2259146bb84e5362eeb775186","_id":"56a7353f682e1cc615f4a6b1","buildingBlocks":[{"user":{"name":"lajdi"},"buildingBlockTree":[{"uid":"a875ed6cf4052448f9e0cc9ff092a76d4","_id":"56a7353f682e1cc615f4a6ae","columns":[{"uid":"a0061fdd2259146bb84e5362eeb775186","_id":"56a7353f682e1cc615f4a6b1","buildingBlocks":[]},{"uid":"abbcf3328854840deb96bb6b101df2b39","_id":"56a7353f682e1cc615f4a6af","buildingBlocks":[{"uid":"a931cf745d4b847ba9cdc7f4b830413be","_id":"56a7353f682e1cc615f4a6b0","source":{"limit":1,"itemsListId":1,"offset":0}}]}],"template":{"name":"Xs12Sm12Md6Lg12DotXs12Sm12Md6Lg12"}}],"_id":"56a7353f682e1cc615f4a6ad","source":{"limit":1,"itemsListId":1,"offset":0},"template":{"numberOfColumns":1},"uid":"a5f6a2fd1c80f49c0b97e0c7994400588"}]},{"uid":"abbcf3328854840deb96bb6b101df2b39","_id":"56a7353f682e1cc615f4a6af","buildingBlocks":[{"_id":"56a7353f682e1cc615f4a6b0","source":{"limit":1,"itemsListId":1,"offset":0},"isHidden":true}]}]}],"_id":"56a7353f682e1cc615f4a6ad","source":{"limit":1,"itemsListId":1,"offset":0},"uid":"a5f6a2fd1c80f49c0b97e0c7994400588","metadata":{"title":"sss"},"title":"sss","url":"sss"};
const removeHiddenObjects = (input) => objectScan(['**[*].isHidden'], {
rtn: 'count',
filterFn: ({ value, gparent, gproperty }) => {
if (value === true) {
gparent.splice(gproperty, 1);
return true;
}
return false;
}
})(input);
console.log(removeHiddenObjects(data)); // counts removals
// => 1
console.log(data);
/* => {
user: { name: 'Bob' },
buildingBlockTree: [ {
uid: 'a875ed6cf4052448f9e0cc9ff092a76d4',
_id: '56a7353f682e1cc615f4a6ae',
columns: [ {
uid: 'a0061fdd2259146bb84e5362eeb775186',
_id: '56a7353f682e1cc615f4a6b1',
buildingBlocks: [ {
user: { name: 'lajdi' },
buildingBlockTree: [ {
uid: 'a875ed6cf4052448f9e0cc9ff092a76d4', _id: '56a7353f682e1cc615f4a6ae', columns: [
{ uid: 'a0061fdd2259146bb84e5362eeb775186', _id: '56a7353f682e1cc615f4a6b1', buildingBlocks: [] },
{ uid: 'abbcf3328854840deb96bb6b101df2b39', _id: '56a7353f682e1cc615f4a6af', buildingBlocks: [
{ uid: 'a931cf745d4b847ba9cdc7f4b830413be', _id: '56a7353f682e1cc615f4a6b0', source: {
limit: 1,
itemsListId: 1,
offset: 0
} }
] }
],
template: { name: 'Xs12Sm12Md6Lg12DotXs12Sm12Md6Lg12' }
} ],
_id: '56a7353f682e1cc615f4a6ad',
source: { limit: 1, itemsListId: 1, offset: 0 },
template: { numberOfColumns: 1 },
uid: 'a5f6a2fd1c80f49c0b97e0c7994400588'
} ]
}, { uid: 'abbcf3328854840deb96bb6b101df2b39', _id: '56a7353f682e1cc615f4a6af', buildingBlocks: [] } ]
} ],
_id: '56a7353f682e1cc615f4a6ad',
source: { limit: 1, itemsListId: 1, offset: 0 },
uid: 'a5f6a2fd1c80f49c0b97e0c7994400588',
metadata: { title: 'sss' },
title: 'sss',
url: 'sss'
}
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#16.0.0"></script>
Disclaimer: I'm the author of object-scan
Related
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
I'm trying to return a new array of objects from the nested properties of another object. This is my current object:
const teams = {
GRYFFINDOR: {
display: 'Gryffindor',
channel: ['#team-gryffindor'],
ui: 'lion',
},
HUFFLEPLUFF: {
display: 'Hufflepuff',
channel: ['#team-hufflepuff'],
ui: '', // empty string on purpose
},
SLYTHERIN: {
display: 'Slytherin',
channel: ['#team-slytherin'],
ui: 'snake',
},
}
Now, I'm trying to return an array of objects like so:
[
{ value: 'lion' },
{ value: '' },
{ value: 'snake' },
]
This is what I have tried:
const teamsUI = [Object.keys(teams).reduce((carry, item) => {
return {...carry, ...{ ['value']: teams[item].ui }}
}, {})];;
However, I get this:
console.log(teamsUI); // returns [ { value: 'snake' }]
What am I doing wrong?
You could do like this:
const teams = {
GRYFFINDOR: {
display: 'Gryffindor',
channel: ['#team-gryffindor'],
ui: 'lion',
},
HUFFLEPLUFF: {
display: 'Hufflepuff',
channel: ['#team-hufflepuff'],
ui: '', // empty string on purpose
},
SLYTHERIN: {
display: 'Slytherin',
channel: ['#team-slytherin'],
ui: 'snake',
},
}
const result = Object.values(teams).map(({ ui }) => ({ value: ui }));
console.log(result);
Beside nested result structure, you could take the values and destructure the wanted property formapping.
const
teams = { GRYFFINDOR: { display: 'Gryffindor', channel: ['#team-gryffindor'], ui: 'lion' }, HUFFLEPLUFF: { display: 'Hufflepuff', channel: ['#team-hufflepuff'], ui: '' }, SLYTHERIN: { display: 'Slytherin', channel: ['#team-slytherin'], ui: 'snake' } },
result = Object.values(teams).map(({ ui: value }) => ({ value }));
console.log(result);
Here is the shortest way to achieve what you want
Object.keys(teams).map(t=>({value: teams[t].ui}))
Now what are you doing wrong?
Take a look at this part
Object.keys(teams).reduce((carry, item) => {
return {...carry, ...{ ['value']: teams[item].ui }}
}, {});
You're reducing your object to a single value with the same key called value. This means that the value of teams[item].ui of the current object in the reduce function will override the previous and hence you'll end up with the last object of that reduce function. You're finally wrapping this last object in an array which gives you what you have.
A better way to achieve that will be to do something like this
Object.keys(teams).reduce((carry, item, index)=>{
carry[index] = { value: teams[item].ui };
return carry;
}, []);
I have an array with a few objects which I'm trying to combine into one single object
[
{ user: { '563D': { pId: '12', seasion: '120', value: true } } },
{ user: { '563D': { pId: '11', seasion: '120', value: false } } },
...
]
pId is unique
seasion is the same for each object (almost never changing)
value can be anything
I want to have something like this:
{
id: '563D',
seasion: '120',
types: {
12: // pId
{
value: true
},
11:
{
value: false
}
}
}
I tried to use reduce and forEach but I wasnt been able to achieve my goals
due to poor understanding of those 2 methods.
EDIT:
forgot about several users input, sorry
[
{
users: {
'563D': [Object],
'4b07': [Object]
}
},
{
users: {
'563D': [Object],
'4b07': [Object]
}
},
{
users: {
'563D': [Object],
'4b07': [Object]
}
}
]
You could use reduce and destructuring to group the objects based on the first key inside user. Then use Object.values() to get the array of each group values:
Get user from each object by destructuring the parameter
Destructure the first entry of the user to get the key (like '563D') and the nested properties separately)
Use the || operator to check if the accumulator already has the id as it's property. If yes, use it. Else assign a new value with { id, seasion, types: {} }. This is using the Shorthand property names.
Update the types with pId as key and { value } as it's value
const input = [{user:{'563D':{pId:'12',seasion:120,value:true}, '4b07':{pId:'12',seasion:120,value:true}}},{user:{'563D':{pId:'11',seasion:120,value:false},'4b07':{pId:'11',seasion:120,value:false}}}]
const output = input.reduce((r, { user }) => {
for(const [id, { pId, seasion, value }] of Object.entries(user)) {
r[id] = r[id] || { id, seasion, types: {} };
r[id].types[pId] = { value };
}
return r;
}, {})
console.log(Object.values(output))
If you have only one unique id in the array, you can simplify the reduce to:
const input = [{user:{'563D':{pId:'12',seasion:120,value:true}}},{user:{'563D':{pId:'11',seasion:120,value:false}}}]
const output = input.reduce((r, { user }) => {
const [id, { pId, seasion, value }] = Object.entries(user)[0];
return { id, seasion, types: { ...r.types, [pId]: { value } } }
}, {})
console.log(output)
My data set:
{
courseID003: {
studentID34: {
assigned: false
dueDate: null
}
studentID34: {
assigned: false
dueDate: null
}
}
courseID007: {
studentID89: {
assigned: true
dueDate: "2018-12-07 15:51"
}
studentID111: {
assigned: true
dueDate: "2018-12-07 15:51"
}
studentID115: {
assigned: false
dueDate: null
}
}
}
Question:
I have a number of course objects that contain student objects. Each course could contain a different number of students.
I need to search every course and every student to see if the assigned property is set to true.
Ideally, I'd like to call a function that performs this search and simply returns true or false. When searching, as soon as an assigned property is found set to true, the search would exit and return true. If all assigned properties are set to false for every student in every course, then return false.
I hope this question makes sense and I hope the data set makes sense/properly formatted ( I am dealing with all objects -- no arrays). All the course objects are in an object.
Thanks in advance for any help!
Here you are!
const data = {
courseID003: {
studentID34: {
assigned: false,
dueDate: null
},
studentID34: {
assigned: false,
dueDate: null
}
},
courseID007: {
studentID89: {
assigned: true,
dueDate: "2018-12-07 15:51"
},
studentID111: {
assigned: true,
dueDate: "2018-12-07 15:51"
},
studentID115: {
assigned: false,
dueDate: null
}
}
}
const search = (data) => {
return Object.keys(data).some(courseKey => {
return Object.keys(data[courseKey]).some(studentKey => {
return data[courseKey][studentKey]['assigned']
})
})
}
console.log(search(data))
Reference: some, Object.keys
You're going to have a bad time searching through that data as written. The real issue is your choice of data structure. In two places, you are using an object when you should be using an array.
Here is the same data but with courses encoded as an array, which is traversable with the rich set of array methods. I've made the same change to the collection of students for each course.
let courses = [
{
id: 'courseID003',
students: [
{
id: 'studentID34',
assigned: false,
dueDate: null
},
{
id: 'studentID34',
assigned: false,
dueDate: null
}
]
},
{
id: 'courseID007',
students: [
{
id: 'studentID89',
assigned: true,
dueDate: "2018-12-07 15:51"
},
{
id: 'studentID111',
assigned: true,
dueDate: "2018-12-07 15:51"
},
{
id: 'studentID115',
assigned: false,
dueDate: null
}
]
}
];
let unassignedStudents = Array.prototype.concat.apply(
[],
courses.map(c => c.students.filter(s => !s.assigned)));
console.log('unassignedStudents:',unassignedStudents);
With this improved data structure, you can find all 'unassigned' students like this:
let unassignedStudents = Array.prototype.concat.apply(
[],
courses.map(c => c.students.filter(s => !s.assigned)));
Hopefully you can see how this change in structure opens new doors for you.
Good luck.
This should do the trick, keep in mind Object.values might require a polyfill (for IE) as well as flatMap (missing in many browsers atm).
const courses = Object.values(data);
const students = courses.flatMap(course => Object.values(course);
const result = students.some(student => student.assigned);
In a project I'm working on I'm calling an rest-api that returns json as follows:
[
{
"id":0,
"username":"someone0",
"areas":[
],
"role":{
"id":2,
"name":"somerole2",
"users":null
}
},
{
"id":1,
"username":"someone1",
"areas":[
],
"role":{
"id":1,
"name":"somerole1",
"users":null
}
},
{
"id":3,
"username":"someone3",
"areas":[
],
"role":1
}
]
As you can see user 'someone1' and 'someone3' both carry the same role object, but the second time it's referenced (at someone3) it is only referenced by it's id.
Because every response from this api is formatted this way, I'm looking for a generic way to fix this (replace the id-reference with the full object) in pure javascript.
Any common solutions to the seemingly common issue?
PS: I'm sorry for the title, I don't know the right term (suggestions are welcome).
You could simply map a function that makes these corrections to the initial value. Something like this should work if I understand what you are going for:
response = response.map( function(x){
if( (typeof x.role) == "number" )
x.role = { id : x.role };
return x;
});
This however will not give you the names and users field. If this is important, I recommend we iterate through the data once and make a list of roles, then go back and fill in the gaps. This would look like:
roles = {};
response.forEach( function(x){
if( (typeof x.role) != "number" )
roles[x.role.id] = x.role;
});
response = response.map( function(x){
if( (typeof x.role) == "number" )
x.role = roles[x.role];
return x;
});
The output of running that on your data is:
[
{ id: 0,
username: 'someone0',
areas: [],
role: { id: 2, name: 'somerole2', users: null }
},
{ id: 1,
username: 'someone1',
areas: [],
role: { id: 1, name: 'somerole1', users: null }
},
{ id: 3,
username: 'someone3',
areas: [],
role: { id: 1, name: 'somerole1', users: null }
}
]