so at the moment I have a collection that looks like: https://i.stack.imgur.com/lxYoA.png . So in this collection it has a list which consists of nested lists. But I basically want to do a for loop that will add all of this to just one list, but what I'm doing below is just adding an empty list to firebase?
In firebase, I've added individual lists to another list as the documents consist of lists which I have done below and it creates the screenshot that I have above.
const snapshot = await fire.firestore()
.collection("groupsCategory")
.doc(groupID)
.collection('events')
.doc(eventID)
.collection("memberPicks").get()
const data = snapshot.docs.map(doc => doc.data())
snapshot.docs.map(doc => doc.data())
const db = fire.firestore();
const likesList = [];
snapshot.docs.forEach((doc) => {
likesList.push(doc.data())
})
As you can see here, I'm trying to get each item in the list and have it in one list and not a list of nested lists. Where am I going wrong?
const arr1 = likesList.flat();
const newArr = [];
for (let i = 0; i < arr1; i++) {
newArr.push(arr1[i])
}
db.collection("eventLikes")
.doc(eventID)
.set({
ActivityLikes: newArr
})
}
As far as I understand your question, you want to set a document which doesn't have a nested list of userLikes. To be able to achieve that you should iterate the data in which you're fetching the objects of userLikes and pushing it into an array. See code below:
var userLikes = [];
const snapshot = await db
.collection("groupsCategory")
.doc(groupID)
.collection('events')
.doc(eventID)
.collection("memberPicks").get()
snapshot.docs.forEach((doc) => {
// Gets the List of `ActivityLikes`
const activityLikes = doc.data().ActivityLikes;
// Iterate the List of `ActivityLikes`
activityLikes.forEach((activityLike) => {
// Push the data object of `userLikes` to the initiated array: `userLikes`
userLikes.push(activityLike.userLikes[0]);
})
})
// This sets the data as follows:
// Map (ActivityLikes) > Array (userLikes) > Map (userLikes Object)
db.collection("eventLikes")
.doc('eventID')
.set({
ActivityLikes: {userLikes}
})
Added some comments on the code above to better understand.
Here's the screenshot of the result:
Take a look at these documentation to better understand working with objects and arrays:
Working with objects
Arrays
Related
I have an array with multiple objects in it. I would like to output the objects onto an HTML page
The image above is how the array is structured. There is an array called reserved items and it has objects in it. I would like to output all the objects in this array, specifically the "Title" element.
here is the code I tried using
let query;
if (lastReservedBook != null) {
query = db.collection("users").where("uid", "==", uid).startAfter(lastReservedBook)
} else {
query = db.collection("users").where("uid", "==", uid);
}
query.limit(100).get().then(querySnapshot=>{
lastReservedBook = querySnapshot.docs[querySnapshot.docs.length - 1];
querySnapshot.forEach(doc=>{
console.log(doc.data())
let data = doc.data();
let row = `<tr>
<td><a onclick="barcode=${data.Barcode}; myFunction(this)">${data.reserved_items}</a></td>
</tr>`;
let table = document.getElementById('myTable')
table.innerHTML += row
document.getElementById("loader").style.display = "none"
})
})
.catch(err=>{
console.log(`Error: ${err}`)
});
This is the output (which I don't want):
I think you are mixing up the documents and the fields.
Making the assumption user IDs (i.e. uid) are unique, the query on db.collection("users").where("uid", "==", uid) will return only one document.
Then, in this unique document, you have a field named reserved_items which is of type Array. More precisely it is an Array of Objects. So you need to loop over the Array elements, not on the QuerySnapshot.
So the following should do the trick:
let query = db.collection('users').where('uid', '==', uid);
query
.get()
.then((querySnapshot) => {
const queryDocumentSnapshot = querySnapshot.docs[0]; //There is only one doc in the QuerySnapshot!!
const reservedItemsArray = queryDocumentSnapshot.data()
.reserved_items;
reservedItemsArray.forEach((obj, index) => {
let row = `<tr><td><a onclick="barcode=${obj.Barcode}; myFunction(index)">${obj.Title}</a></td></tr>`;
table.innerHTML += row;
});
// ...
})
.catch((err) => {
console.log(`Error: ${err}`);
});
It's not 100% clear to me:
What you want to display for the link text... I've used the Title with obj.Title but you may adapt.
What you want to do with myFunction(this). You should probably use the index, e.g. like myFunction(index), which indicates the position of the element in the Array.
Why you limit to 100. It's up to you to adapt the above code if you only want to display 100 lines in the table. But again, if you need to limit the number of elements to be displayed, this limit shall apply to the elements of the Array, not to the query.
Try this:
console.log(JSON.stringfy(doc.data()));
JSON.parse(JSON.stringify(doc.data()) , this shall work if the issue is that you are not seeing the data in the object ( you might need a loop )
Whenever I select a person from the list it grabs the id of a name and stores it an array with map.
I then have a string literal which gets populated with the ID.
const id = value.map(person => person.value)
console.log('from',id)
current output:
[u29219]
withe the results looking like this:
const results = await verifiedGet(`get_user/?$u29219?admin_form=True`, user.user)
and then if I add another person the array would look like this
[u29219, u302932]
results:
const results = await verifiedGet(`get_user/hello?$u29219,u302932?admin_form=True`, user.user)
When a user is added to the array I want to be able to iterate through the results with the ID only populating once if a user is selected twice
const results = await verifiedGet(`get_user/?$u29219?admin_form=True`, user.user)
const results = await verifiedGet(`get_user/?$u302932?admin_form=True`, user.user)
is this possible to do so?
I created a sandbox for a better understanding
https://codesandbox.io/s/modest-star-fegy7
I would take your unique id array and use Array.Join() to combine them with commas.
// Map the ids, and filter unique
const uniqueIds = value
.map((person) => person.value)
.filter((value, index, self) => self.indexOf(value) === index);
// Join the Ids and separate them with commas
const commaSeparatedIds = uniqueIds.join[','];
// Don't forget to make them safe for a URL
// You may be able to skip this if you are certain
// your string is safe
const uriSafeIds = encodeURIComponent(commaSeparatedIds);
// Finally, interpolate into your string literal template
// I wasn't sure if the extra $ in your code was intended
// so I left it in.
const url = `get_user/hello?$${uriSafeIds}?admin_form=True`;
const results = await verifiedGet(url, user.user);
In the line const id = user.map((person) => person);, we are looping through the 'user' array and for each iteration, we are just returning the element in that iteration. The 'map' function will return an Array of what will be returned in each iteration. So, basically, we are re-creating the user array in id.
const user = ["u29219", "u302932"];
// 'user' array is re-created here
const id = user.map((person) => person);
console.log("from", id);
const results = `get_user/?${id}admin_form=True`;
If your intention was to form the results string with each element in the array, this would be a way to do it using forEach:
user.forEach((person) => {
const results = `get_user/?${person}$admin_form=True`;
// remaining logic
});
Code-Sandbox
Can make the array unique, then concat or join to get the expected output.
const user = ["u29219", "u302932", "u302932"];
const uniqueItems = [...new Set(user)];
console.log("current uniqueItems", uniqueItems);
let results = "";
uniqueItems.forEach((person) => {
results = results.concat(`get_user/?${person}$admin_form=True `);
});
console.log("current output", results);
This code does a great job of fetching and rendering everything within the JSON array, but what if I am interested in only listing the objects with a particular key-value (like gender)? Would that happen during the fetch or the render?
const URL = "https://ghibliapi.herokuapp.com/people";
const main = document.getElementById("main");
main.innerHTML = "<p>Loading...";
fetch(URL).then((response) => response.json()).then((people) => main.innerHTML = getListOfNames(people));
const getListOfNames = (people) => {
const names = people.map((person) => `<li>${person.name} - ${person.gender} </li>`).join("\n");
return `<ul>${names}</ul>`;
};
The ideal case would be using GraphQL so you only fetch the data fields you need based on your criteria, in this case, there is no difference between changing the getListOfNames function for just outputting a person when its person.gender matches your criteria or simply passing to it a filtered array of people after fetching them all
You would have to configure the api endpoint to accept filters if you'd like to return filtered results. Otherwise, you'd filter on render.
With underscore you'd do _.where(response, {gender: 'male'})
I am trying to get a list of repositories, that is my code does a search for repositories with a filter
The Javascript gets a result, with multiple items that contain the data for each repository that fit the filter using the URL: https://api.github.com/search/repositories?q=piccolowen+in:name.
I can do console.log(result.items[0].name) to get the first repository's name value, but I want get all of the repositories from the search printed to the console. I also want the code to be able to print all of the repositories and their values no matter how many repos fit the filter.
Here is the current code I want to add on to:
window.onload = func()
async function func() {
const url = 'https://api.github.com/search/repositories?q=piccolowen+in:name'
const response = await fetch(url);
const result = await response.json();
const apiresult = document.getElementById('thisisanid')
console.log(result)
}
Any ideas on how I could do this?
EDIT:
I found the answer to my problem using a while loop from this question: Get total number of items on Json object?
const resultLength = Object.keys(result.items).length
var arrnum = 0
while (arrnum < resultLength) {
//execute code
}
EDIT 2:
The code in my previous edit will crash a page. Still working on a solution for this huge bug.
Since results.items returns an array of objects, you can use Array.prototype.map to return all the item names, i.e.:
result.items.map(item => item.name)
If you want to simply filter out some properties, you can also do object destructuring. Let's say you want an array of items that only contain their name, id, and description, then you can do this:
result.items.map(({ name, id, description }) => ({ name, id, description }))
async function func() {
const url = 'https://api.github.com/search/repositories?q=piccolowen+in:name'
const response = await fetch(url);
const result = await response.json();
// Returns an array of item names
console.log(result.items.map(item => item.name));
// Returns an array of object with selected keys
console.log(result.items.map(({ name, id, description }) => ({ name, id, description })));
}
func();
The array has map function, which accepts a callback function. It iterate through all the elements and call the callback function and push data to the newly created array.
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
More:
Array.map
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
window.load = main();
const nameMapper = (item) => item.name;
const liMapper = (item) => `<li>${item.name}</li>`;
async function main() {
const url = "https://api.github.com/search/repositories?q=piccolowen+in:name";
const result = await fetch(url).then((x) => x.json());
const names = result.items.map(nameMapper);
const apiresult = document.getElementById("thisisanid");
apiresult.textContent = names;
const ul = document.getElementById("list");
ul.innerHTML = result.items.map(liMapper).join("");
}
#list li{
list-style: none;
padding: 5px 10px;
border: 1px solid black;
max-width: 400px;
}
<div id="thisisanid"></div>
<ul id="list">
</ul>
You can use like!
let list = document.getElementById('list');
let htmlTemplate = result.items.map(function(item) {
return item.name
}).join("")
list.insertAdjacentHTML('beforeend', htmlTemplate)
or you can use template literal
foe example
when you returning value in items.map()
return `${item.id}: ${item.name}`
I have an array of Groups s.t. each Group has many Users
I want to return all (unique) Users for a given array of Groups.
So far, I have
let actor = await User.query().findById(req.user.id).eager('groups') // find the actor requesting
let actor_groups = actor.groups // find all groups of actor
if (actor_groups.length > 1)
var actor_groups_users = actor_groups[0].user
for (let i = 0; i < actor_groups.length; i++) {
const actor_groups_users = actor_groups_users.concat(actor_groups[i]);
}
console.log('actor groups users is', actor_groups_users);
else
// return users from the first (only) group
which returns the error: actor_groups_users is not defined
Feels like a roundabout way to do this. Is there a way to just combine actor_groups into a single combined group?
Here we can cycle through, adding users if not already in the array, using .forEach() and .includes().
This is assuming that group.user is an Array of users.
let users = [];
// check that actor_groups has items in it
if (actor_groups && actor_groups.length > 1) {
// cycle through each actor_group
actor_groups.forEach( group => {
// check if we have a 'user' array with items in it
if (group.user && group.user.length > 1) {
// cycle through each user in the group
group.user.forEach( user => {
// check if we already have this user
// if not, add it to users
if (!users.includes(user)) {
users.push(user);
}
}
}
}
}
You can simply do this:
const allGroupsArrs = actor_groups.map(({ user }) => user);
const actor_groups_users = [].concat(...allGroupArrs);
Or, you could simply use the .flat() method, which is not yet officially part of the ES standard, but is on its way there and has browser support outside of IE:
const allGroupsArrs = actor_groups.map(({ user }) => user);
const actor_groups_users = allGroupArrs.flat();
Also, the above would result in duplicate values in actor_groups_users if there are people who are in multiple groups. You can remedy this (assuming the array elements are primitive values) using a Set:
const unique_users = [...new Set(actor_groups_users)];
The most efficient way I can think of is
const users = [...new Set([...actor_groups].flatMap(el => el.user))]
I used this example:
const actor_groups = [{user: ['ok','boom']}, {user: 'er'}]
console.log([...new Set([...actor_groups].flatMap(el => el.user))])
//output: ["ok", "boom", "er"]