How to get all the 'items' of an API array from GitHub? - javascript

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}`

Related

Adding firebase documents to a list

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

How to delete an element from array in react?

I have two functions , one of them adds an item in array and the other one delete from that array using React JS (hooks).[Both are handler of click event].
What I have works incorrectly.
``id`` comes from ``contact.length`` and I deleted it with``contacts.splice(id, 1)``.
I dont have any idea why it has this problem.
it doesnt delete what would be clicked but a random one.
function handleAddRecord(nameValue, phoneValue) {
setContacts([...contacts , {
id : contacts.length,
name : nameValue,
phone : phoneValue
}])
}
function handleDelete(id) {
console.log("manager", id);
const newContacts = contacts.splice([id],1);
setContacts([...newContacts]);
}
One of the issue on the implementation is id generation keeping it array length could lead to issue as you delete and add elements there could be scenarios where there is same id for multiple items.
One of most widely used generator is uuid https://www.npmjs.com/package/uuid
Usage
const uuid = require("uuid");
uuid.v4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
Now use this in your implementation
Add Operation:
const handleAddRecord = (nameValue, phoneValue) => {
const newRecord = {
id: uuid.v4(), // This should be unique at all times
name: nameValue,
phone: phoneValue,
};
setContacts([...contacts, newRecord]);
};
Delete Operation:
Use filter rather than splice as for splice you'll need to find the index of the element with id. But with Filter it can be done is a single line
const handleDelete = (id) => {
setContacts(contacts.filter(item => item.id !== id));
};
Here we're assuming that id is the index of the element to be removed.
The splice function returns the removed elements, thus is not useful to take its result. Instead, make a copy of the array first, then remove the undesired element:
function handleDelete(id) {
console.log("manager", id);
const newContacts = [...contacts];
newContacts.splice(id,1);
setContacts(newContacts);
}
That's because splice alters the array itself.
More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Ok, id return index of current map?
Follow this example:
const assoc = [...contacts];
assoc.splice(id, 1);
setContacts(assoc);
You can delete the item by finding its index from array.
For Example:
function handleDelete(id) {
console.log("manager", id);
const index = contacts.findIndex((x) => x.id === id);
const newContacts = [
...contacts.splice(0, index),
...contacts.splice(index + 1),
];
setContacts(newContacts);
}
You need undestand, every time when i'll remove a item from a array of a index, that this index has use unique key... When React remove a item 6 (a example) this is remove of array first, and when react re-render function react can delete another component, because a array exist key 6, wehn you have more 6 item from array... Understand?
Follow a example:
import React, { useState } from 'react';
function User(data) { // data is a array
const [contacts, setContacts] = useState(data); // data return a list of contacts
/* contacts result a array object and has the following attributes
[{
name: 'Cael',
tel_number: '+55 11 9999-999',
e_mail: 'user#example.com',
! moment: "2021-06-15T05:09:42.475Z" // see this a date in ISO string
}]
*/
// about moment atribute:
// this atribute result when use `new Date().toISOString()`
// and this value is added in the moment that create a object in array list
// It's mean that every time result a unique key
const deleteFn = (val) => { // val result index of component and item array
const assoc = [...contacts];
assoc.splice(val, 1);
setContacts(assoc);
}
return (
<div>
{!!contacts.length &&
contacts.map((assoc, i) => { // variable i result in your id
const { moment, name, e_mail, tel_number } = assoc; // moment use a unique key
return (
<li key={moment}>
<span>{name}</span>
<span>{e_mail}</span>
<span>{tel_number}</span>
<button type="button" onClick={() => deleteFn(i)}>Delete</button>
</li>
);
})}
</div>
);
}
export default User;
I hope, this helpfull you!

Dynamically populate string literally templates

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);

How to push all values into array and get them with javascript

I have my previous question in this link my question
I asked to push all values into an array and show to the HTML. They responded well but it showing only one value(zip1) into an array and get them to HTML.
So i want to get that all values like zip1,zip2, distance, weight based on the group number.
I tried but answer not came
my code altered from previous answer.
const array = [[{"loc":{}},{"distance":6.4},{"zip1":"06120"},{"zip2":"06095"},{"group":1},{"weight":1119}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":2},{"weight":41976}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":1},{"weight":41976}]];
const merged = array.map((r, a) =>{
const { group } = a.find(n => n.group)
const { zip1 } = a.find(n => n.zip1)
r[group] = r[group] || []
r[group].push({Zip1:zip1})
const { zip2 } = a.find(n => n.zip2)
r[group].push({Zip2:zip2})
const { weight } = a.find(n => n.weight)
r[group].push({weight:weight})
const { distance } = a.find(n => n.distance)
r[group].push({distance:distance})
return r;
},{})
const output = document.getElementById('output');
Object.entries(merged).forEach(([group, zips]) => {
const h1 = document.createElement('h1');
h1.innerHTML = "group " + group
const span = document.createElement('span');
span.innerHTML = `Zip1 - ${zips.zip1},${zips.zip2},${zips.weight},${zips.distance} (in group - ${group})`;
output.appendChild(h1)
output.appendChild(span)
})
My expected output(but I need to show this in google map infowindow.I just showing example content)
Methodology
Convert your 2D array into a 1D array, so instead of having arrays as inner items you will have objects. This is done through the arrToObj function
Convert your zip values from string to array. This is done to facilitate their _concatenation in the future. Done through the zipToArr function
Group your array of objects under one object. In order to do that we promote the group key and concatenate zip1/zip2 with other objects from the same group. Refer to the grouper function
Get the grouped objects using Object.values on the previous aggregate. We already have the group key in them so we don't need the parent key anymore
Format your values into HTML elements based on their respective keys. This will facilitate generating the HTML in the end since we'll have the elements ready. Done with html and format functions
Render your HTML by iterating on the previously generated array. In each iteration create a container div that will hold the group. The container will help styling its first element group
Implementation
const array = [[{"loc":{}},{"distance":6.4},{"zip1":"06120"},{"zip2":"06095"},{"group":1},{"weight":1119}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":2},{"weight":41976}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":1},{"weight":41976}]];
// Data processing functions
const arrToObj = arr => arr.reduce((a, c) => ({ ...a, ...c}), {});
const zipToArr = x => ({...x, zip1: [x.zip1], zip2: [x.zip2]});
const grouper = (a, c) => {
delete c.loc;
delete c.distance;
if (a[c.group]) {
a[c.group].zip1.push(...c.zip1);
a[c.group].zip2.push(...c.zip2);
return a;
} else {
return {...a, [c.group]: c}
}
};
// HTML utilities
const html = (k, v) => {
const it = document.createElement('p');
it.innerHTML = `${k} ${v}`;
return it;
}
const format = g => Object.keys(g).sort().reduce((a, c) => ({...a, [c]: html(c, g[c])}), {});
// Actual processing
const data = array.map(arrToObj).map(zipToArr).reduce(grouper, {});
const dataWithHTML = Object.values(data).map(format);
// Rendering
const display = document.getElementById('display');
dataWithHTML.forEach(it => {
const container = document.createElement('div');
Object.values(it).forEach(v => container.appendChild(v));
display.appendChild(container);
});
p:first-of-type {
font-size: 36px;
font-weight: bold;
margin: 0;
}
p {
text-transform: capitalize;
}
<div id="display"></div>

Filter/Reject Array of strings against multiple values using underscore

I'd like to _.filter or _.reject the cities array using the filters array using underscore.
var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ... ]
var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
My progress so far:
var filterList;
if (reject) {
filterList = angular.copy(cities);
_.each(filters, (filter) => {
filterList = _.reject(filterList, (city) => city.indexOf(filter) !== -1);
});
} else {
filterList = [];
_.each(filters, (filter) => {
filterList.push(_.filter(cities, (city) => city.indexOf(filter) !== -1));
});
}
filterList = _.flatten(filterList);
return filterList;
I'd like to DRY this up and use a more functional approach to achieve this if possible?
A somewhat more functional version using Underscore might look like this:
const cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany',
'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou',
'China/Beijing', 'China/Baotou', 'China/Hohhot']
const filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
var inList = names => value => _.any(names, name => value.indexOf(name) > -1);
_.filter(cities, inList(filters));
//=> ["USA/Akron", "USA/Albuquerque", "China/Fuzhou", "China/Baotou"]
_.reject(cities, inList(filters));
//=> ["USA/Aberdeen", "USA/Abilene", "USA/Albany",
// "China/Guangzhou", "China/Beijing", "China/Hohhot"]
I'm using vanilla JavaScript here (some() and filter()) but I hope you get the idea:
const isValidCity = city => filters.some(filter => city.indexOf(filter) > -1)
const filteredCities = cities.filter(isValidCity)
Please note that this is a loop over a loop. So the time complexity is O(n * m) here.
In your example all city keys share the same pattern: country + / + city. Your filters are all an exact match to the city part of these names.
If this is a certainty in your data (which it probably isn't...), you could reduce the number of loops your code makes by creating a Map or object that stores each city per filter entry:
Create an object with an entry for each city name
Make the key the part that you want the filter to match
Make the value the original name
Loop through the filters and return the name at each key.
This approach always requires one loop through the data and one loop through the filters. For small array sizes, you won't notice a performance difference. When one of the arrays has length 1, you'll also not notice any differences.
Again, note that this only works if there's a constant relation between your filters and cities.
var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ]
var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
const makeMap = (arr, getKey) => arr.reduce(
(map, x) => Object.assign(map, {
[getKey(x)]: x
}), {}
);
const getProp = obj => k => obj[k];
const getKeys = (obj, keys) => keys.map(getProp(obj));
// Takes the part after the "/"
const cityKey = c => c.match(/\/(.*)/)[1];
const cityMap = makeMap(cities, cityKey);
const results = getKeys(cityMap, filters);
console.log(results);
Since you seem to be using AngularJS, you could utilize the built-in filter functionality. Assuming both the cities and filters array exist on your controller and you're displaying the cities array using ng-repeat, you could have something like this on your controller:
function cityFilter(city) {
var cityName = city.split('/')[1];
if (reject) {
return filters.indexOf(cityName) === -1;
} else {
return filters.indexOf(cityName) > -1;
}
}
And then in your template, you'd do something like this:
<div ng-repeat="city in cities | filter : cityFilter"></div>
Of course you'd have to modify your syntax a bit depending on your code style (for example, whether you use $scope or controllerAs).

Categories