quick question about Firebase lists.
I'm trying to access multiple lists to iterate through that are in a single object. The object looks like this:
user : {
playlists: {}, // List 1
joined: {}, // List 2
favorites: {} // List 3
}
Now I know I can access all three lists by doing three separate requests like:
firebase.list('user/playlists');
firebase.list('user/joined');
firebase.list('user/favorites');
But I'm trying to clean up my code and do it all in one by using:
firebase.list('user');
Then accessing all the lists it returns inside of the user. The only problem is, the lists aren't returned with keys, it just says "object" instead. When I use firebase.object('user') the keys are available.
This is what it looks like when I request 'user' as a list:
As you can see the key is inside the object and not outside of it ($key). How do I go about accessing these objects like I would if the key was actually the key (eg. user.playlists).
For anyone interested I figured it out. I used the .map() to convert parts of the object into arrays.
getPlaylist(_playlistId: string, _query: Object = {}) {
return this._database.object(`playlists/${_playlistId}`, _query).map(
result => {
return {
info: result.info,
leader: result.leader,
favorites: (result.favorites) ? Object.values(result.favorites) : [],
followers: (result.followers) ? Object.values(result.followers) : [],
songs: (result.songs) ? Object.values(result.songs) : []
};
}
);
}
For example, favorites is an object that I want to be returned as a list:
Check if exists If exists, map obj to array else assign to empty array
(result.favorites) ? Object.values(result.favorites) : []
Related
This question already has answers here:
Find object by id in an array of JavaScript objects
(36 answers)
Closed 2 years ago.
I have a file with 1000s of json rows like below. Im having difficulties locating a specific key in the array.
example json:
{"connection":"98374"
,"db":"8",
,"timestamp":"159905411631"
,"event":"DataCatch"
,"data":[{"key":"ruleid","value":"111"}
,{"key":"responseid","value":"155-response-4"}
,{"key":"responsetype","value":"Capture"}
,{"key":"reason","value":"ClientVisit"}
,{"key":"subreason","value":""}
,{"key":"score","value":"0.00"}
,{"key":"comment","value":""}]
}
I need to be able to find the "reason" key in the "data" array and replace the "value" with "test". The "data" array doesn't always appear on every json row, only when the "event" "dataCatch" is present.
I can parse it into a variable but I can only call the "data" array as a whole. Any ideas how to target specific values in an array?
Having a little trouble with this in Typescript.
There are any number of ways to go about this, but here's one.
First, parse your JSON into an array of objects.
Each element of the array will then look something like this:
{
connection: '98374',
db: '8',
timestamp: '159905411631'
event: 'DataCatch',
data: [
{ key: 'ruleid', value: '111' },
{ key: 'responseid', value: '155-response-4' },
{ key: 'responsetype', value: 'Capture' },
{ key: 'reason', value: 'ClientVisit' },
{ key: 'subreason', value: '' },
{ key: 'score', value: '0.00' },
{ key: 'comment', value: '' },
],
}
Let's call our array of objects allData, so we can refer to it later.
Now we can begin our "surgery".
We'll work from the inside-out, first looking at what needs to be done to a specific entry in an element's data array.
Here's a function that will do just what we need:
function updateReason(entry) {
if (entry.key === 'reason') {
return { ...entry, value: 'test' };
} else {
return entry;
}
}
This function checks if the provided entry has a key with a value of 'reason', and -- if so -- returns a new entry that is identical to the provided one except its value is 'test'.
How can we use this to update an entire data array (in an entry that has data, that is)?
We simply delegate the work to our dear friend map:
function updateData(data) {
// or simply `data.map(updateEntry);`
return data.map(entry => updateEntry(entry));
}
We're slowly working our way "outwards".
What about updating an entire entry in our big allData array (which may or may not contain data)?
// I've called such an entry a "collection", because I already used the name
// "entry" above :(
// A "collection" is just an entry in the big `allData` array.
function updateCollection(collection) {
if (collection.event === 'DataCatch') {
return {
...collection, // Leave everything else the way it is
data: updateData(collection.data), // But update the `data` array
};
} else {
return collection;
}
}
So close.
The last thing we need to do is apply this transformation to every element of our parsed allData array:
// or `allData.map(updateCollection);`
const updatedData = allData.map(collection => updateCollection(collection));
Also:
Q: Wouldn't it be cheaper to mutate the entry?
A: It would be cheaper, but not that much cheaper, due to a large amount of "structural sharing" that occurs here. I would recommend this approach unless you either need to mutate your input for some reason, or performance requirements demand it.
You need to map over the data key in your data variable like this.
data.data = data.data.map((item) => {
if (item.key === "reason") {
item.value = "test";
}
return item;
});
the data key is an array of values, so you need to loop through it and compare the value of the key property to the value you are looking for, if it matches then you can update the value property
https://codesandbox.io/s/angry-shirley-1gh83?file=/src/index.ts:666-782
I'm actually working on React Native which is based on React.js framework. So what I'm trying to here is create an object which has all the filter parameters in it from multiple arrays.
eg.
//This is not my Filter Object.
//A state object in React Native is where you define your variables.
state = {
holder: [], //Holds temp data which will be filled with filtered items
allData: [], //Holds data of all the items
//My filter parameters
location: "",
category: [],
offers: [],
}
For easier understanding, I've filled my filter parameters for you: location: "England", category: ["Mens","Womens"] and offers: ["20%", "40%", "60%"]
This is how my arrays would look like if all the 3 filter conditions have values assigned to them. Now this is the filter function that I'm applying for my array:
filterProducts = () => {
//Packing all of my filter conditions in one Filter Object
let query = {
filter1: this.state.location,
filter2: this.state.category,
filter3: this.state.offers,
}
//Filter Logic on my Holder Array
const filteredData = this.state.allData.filter(function (item) {
return Object.entries(query).every(([key, value]) =>
value.includes(item[key])
);
});
console.log("Result: Filtered Array", filteredData);
this.setState({ holder: filteredData });
}
For your easier understanding, my query object looks like:
let query = {
filter1: "England",
filter2: ["Mens","Womens"],
filter3: ["20%", "30%", "40%"],
}
So now what is my question? My filter logic only works if my Filter Object query looks like above. That is if all 3 filter parameters have some values assigned to them.
My filter logic breaks and returns me a [] array, if my Filter Object query becomes like this:
let query = {
filter1: "England",
filter2: ["Mens","Womens"],
filter3: [], //Blank array inside object because user didn't pick any values
}
So my question to you all is:
How to remove a blank array from an Object?
Is there a way to change my filter logic which will skip filter3 if it's array is blank? Meaning, without removing blank arrays from my Filter Object, filter the array with only paramters which have some value assigned to them?
Something like this should work, I reckon.
You could use Object.keys to iterate over your values and append to new object according to the condition.
You could check the example here
const onlyData = {}
Object.keys(query).forEach(filterKey => {
const filterValue = query[filterKey];
if (filterValue.length) onlyData[filterKey] = filterValue;
})
Basically I have got an object, which will be multi-dimensional and the properties could be named anything and it could have many dimensions.
At some point I will need to append/splice a property within this object, from code which won't know it's position.
So, an example object:
let obj = {
response: {
locations: {
data: [
0: Object,
1: Object,
2: Object,
]
}
},
endpoint: null
}
I need to splice out data.locations.data[1], the only information I have is the below array and the index. Obviously I will know the first property will be response.
['locations','data']
index: 1
Edit:
My mistake, the data property has an array value not an object!
You can use Array#reduce() and pass in obj.response as the start value to get at the nested parent which based on the array shown would be obj.response.locations.data.
Then splice() the indexed item in that parent or do whatever other modifications are needed
const arr = ['locations','data'],
index= 1,
obj = {
response: {
locations: {
data: [{id:1},{id:2}, {id:3}]
}
},
endpoint: null
}
const targetArr = arr.reduce((a,c)=> (a[c]), obj.response);
targetArr.splice(index,1);
console.log(obj)
I need to find nested objects in MongoDb using the Node.js Driver.
I'm having trouble accessing nested properties when the property name is dynamic. Here's my code:
//This gives expected results but "name1" isn't dynamic
collection.find({ 'followers.name1': { $exists: false } })
//Here's what I tried that does not give expected results
const username = "name1"
let query = { followers: {} }
query.followers[username] = { $exists: false }
collection.find(query)
Here's an example of the database structure:
{
"_id":"xxxxxxxxxxx",
"dateAdded":"2017-09-20T08:36:40.325Z",
"followers":{
"name1":{
"followedOn":"2017-09-20T08:36:40.325Z",
"unfollowedOn":null
},
"name2":{
"followedOn":"2017-09-20T08:36:40.325Z",
"unfollowedOn":null
}
}
}
Edit: my question is not a duplicate of the one marked as a duplicate. MongoDb find() argument is not an object literal. That's the whole point of my question, using like it like an object literal doesn't work.
I found the solution myself in the end. The key needs to be a string so you need to do:
const username = 'name1'
let query = {}
query['followers.'+username] = { $exists: false }
collection.find(query)
{
_id:xxxxstoreid
store:{
products:[
{
_id:xxxproductid,
name:xxx,
img:url,
}
]
}
}
since i cannot predict the request for update, params may have just name or it may have both.
here is my query,it updates successfully but it removes other fields if they are not present in the params.
eg:
var params={_id:xxid,name:'xxx',img:'xxx'}
or
var params={_id:xxid,name:'xxx'}
in this case if params have just name it removes img field and updates.
User.update({'store.products._id':params._id},{$set:{"store.products":params}},callback);
You need to supply the multiple keys to $set with the positional $ operator to update both matched keys.
I prefer the modern ES6 way of object manipulation:
let params = { "_id" : "xxxproductid", "name" : "xxx", "img" : "yyy" };
let update = [
{ 'store.products._id': params._id },
{ "$set": Object.keys(params).filter(k => k != '_id')
.reduce((acc,curr) =>
Object.assign(acc,{ [`store.products.$.${curr}`]: params[curr] }),
{ })
}
];
User.update(...update,callback);
Which would produce the call to MongoDB as ( with mongoose.set('debug', true) ) turned on so we see the request:
Mongoose: users.update({ 'store.products._id': 'xxxproductid' }, { '$set': { 'store.products.$.name': 'xxx', 'store.products.$.img': 'yyy' } }, {})
Where basically you take your input params and supply the _id as the first argument for the "query" :
{ 'store.products._id': params._id },
The rest takes the "keys" from the object via Object.keys which makes an "array" which we can "filter" with Array.filter() and then pass to Array.reduce to transform those keys into an Object.
Inside the .reduce() we call Object.assign() which "merges" objects with the given keys, generated in this form:
Object.assign(acc,{ [`store.products.$.${curr}`]: params[curr] }),
Using the template syntax to assign the "current" (curr) "key" into the new key name, again using the ES6 key assignment syntax []: which allows variable names in object literals.
The resulting "merged" object is passed back to be assigned to the "root" object where $set is used for the key of the update, so the "generated" keys are now children of that.
I use an array for the arguments purely for debugging purposes, but then that also allows cleaner syntax on the actual .update() using the "spread" ... operator to assign the arguments:
User.update(...update,callback);
Clean and simple, and some JavaScript techniques that you should learn for object and array manipulation. Mostly since the MongoDB query DSL is basically "Objects" and "Arrays". So learn to manipulate them.
function updateProducts(params) {
var query = {'store.products': {$elemMatch: {_id: params._id}}}
var updateObject = null;
if (params.name && params.img) {
updateObject = {$set: {'store.products.$': params}}
} else if(params.name && !params.img) {
updateObject = {$set: {'store.products.$.name': params.name}}
} else if (params.img && !params.name) {
updateObject = {$set: {'store.products.$.img': params.img}}
}
User.update(query, updateObject, callback)
}
The below query will use $ positional operator to locate the array element at index found from matching the array by _id in the query document followed by updating the fields for the element with the params values.
var params = {_id:1, name:'xxx',img:'yyy'};
var id = params['_id']; // Get id to locate matching array index
delete params['_id']; // Remove id from the object
Object.keys(params).forEach(function (key){params['store.products.$.'+key] = params[key]; delete params[key];}) // Transforms remaining object keys to include the positional $ placeholder to { "store.products.$.name" : "xxx", "store.products.$.img" : "yyy" }
User.update('store.products._id':id},{$set:params},callback);