How can get a count with nested objects with a certain property? - javascript

Okay so I'm using angular to get a json saved to my computer to recreate a github gradebook.
I can get the data with my $http request but for the love of me all I want is to get a count of the number of issues with the label "Not Yet".
Here is the javascript:
$http.get('/api/github/repos/issues/all_issues/00All.json')
.then(function(response) {
console.log(response.data[0]);
var counter = 0;
for(var index = 0; index < response.data.length; index++) {
if(response.data[index].labels[0].name == "Not Yet") {
counter++;
};
};
console.log(counter);
});
That's the latest try, I also tried using lodash to get it earlier:
$http.get('/api/github/repos/issues/all_issues/00All.json')
.then(function(response) {
console.log(response);
mile.notYet.width = _.forEach(response.data, function(n){
var counter = 0;
if(_.result(_.find(n.labels[0], 'name')) == "Not Yet") {
counter++;
}
console.log(counter);
counter = ((counter/10) * 100) + '%';
});
});
This is a bit of the json data:
[
{
"url": "https://api.github.com/repos/TheIronYard--Orlando/2015--SUMMER--FEE/issues/11",
"labels_url": "https://api.github.com/repos/TheIronYard--Orlando/2015--SUMMER--FEE/issues/11/labels{/name}",
"comments_url": "https://api.github.com/repos/TheIronYard--Orlando/2015--SUMMER--FEE/issues/11/comments",
"events_url": "https://api.github.com/repos/TheIronYard--Orlando/2015--SUMMER--FEE/issues/11/events",
"html_url": "https://github.com/TheIronYard--Orlando/2015--SUMMER--FEE/issues/11",
"id": 73013825,
"number": 11,
"title": "00 -- Brace Yourself -- BEN GRIFFITH",
"user": {
"login": "Epicurean306",
"id": 11682684,
"avatar_url": "https://avatars.githubusercontent.com/u/11682684?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/Epicurean306",
"html_url": "https://github.com/Epicurean306",
"followers_url": "https://api.github.com/users/Epicurean306/followers",
"following_url": "https://api.github.com/users/Epicurean306/following{/other_user}",
"gists_url": "https://api.github.com/users/Epicurean306/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Epicurean306/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Epicurean306/subscriptions",
"organizations_url": "https://api.github.com/users/Epicurean306/orgs",
"repos_url": "https://api.github.com/users/Epicurean306/repos",
"events_url": "https://api.github.com/users/Epicurean306/events{/privacy}",
"received_events_url": "https://api.github.com/users/Epicurean306/received_events",
"type": "User",
"site_admin": false
},
"labels": [
{
"url": "https://api.github.com/repos/TheIronYard--Orlando/2015--SUMMER--FEE/labels/Not%20Yet",
"name": "Not Yet",
"color": "e11d21"
}
],
As you can see the labels property is an object, nested in an array, nested in an object, nested in an array, real lovely. Putting labels[0] results in an error for me each time and doesn't get me a count. Can anybody tell me where I'm messing up please? Thank you!

If you need a solution that includes lodash, which is much more performant than the native high order functions then you can try this solution below:
var size = _(response.data)
.pluck('labels')
.flatten()
.where({ name: 'Not Yet' })
.size();
UPDATE:
If you want it to be more reusable, you can save a reference for a cloned chained sequence and simply supply another array for that cloned sequence.
var data1 = [/*array from data1*/];
var data2 = [/*array from data2*/];
var notYetSequence = _(data1)
.pluck('labels')
.flatten()
.where({ name: 'Not Yet' });
notYetSequence.size(); // returns data 1 count
notYetSequence.plant(data2).size(); // returns data 2 count

You don't need lodash for the task
var cnt = response.data
.map(function(i) { return i.labels; })
// here we extract labels object only (and get an array of arrays of objects)
.map(function(i) { return i.filter(function(l) { return l.name == 'Not yet'; }).length; })
// then for every nested array we return a number of items with
// Not Yet names (and get an array of numbers)
.filter(function(c) { return c > 0; })
// then we filter issues that don't have one (and still get an array of numbers)
.length;
// and finally get length (which is a number)

As a comparison, a plain for loop looks like:
var data = response.data;
var count = 0;
var re = /not yet/i;
for (var a, i=0, iLen=data.length; i<iLen; i++) {
a = data[i].labels;
for (var j=0, jLen=a.length; j<jLen; j++) {
if (re.test(a[j].name)) ++count;
}
}
So really not a lot of code either way, the for loop will be compatible with every browser ever (though using xmlHTTPRequest means at least ed 3+) and fastest… untested of course. ;-)

Related

Remove all duplicate object elements from array

This one's been giving me problems for a week, cross my fingers one of you can help me here...
This application was built on Laravel and the front scaffolded using Vue.
Thing is I have an array of objects that is supposed to be sent to the backend in order for it to be stored in a database. Thing is this is an editor and the idea is not reload the page every time something is changed, so here comes my problem...
The way of getting the information is through window.postMessage(), so it seems the information lingers on even after saving, since the page behavior is for it to not reload, I have tried emptying the array after firing the save function. Now it works the first time because the array is empty so there's nothing to compare it to, it also works the second time, but from the third time on, it duplicates some of the objects inside and stores them in DB.
Here's my code:
saveNewSettings() {
//THIS IS THE ARRAY I NEED TO EMPTY (ALREADY DECLARED IN THE DATA OBJECT)
/* this.newItems = [
{ id="123", name="a", otherProps="someProps" },
{ id="456", name="ab, otherProps="someProps" },
{ id="789", name="c", otherProps="someProps" },
]
*/
//THIS IS THE AN EMPTY ARRAY I'M USING TO COMPARE LATER ON... (ALREADY DECLARED IN THE DATA OBJECT)
// this.newlyCreatedItems = [];
if ( !this.newlyCreatedItems.length ) {
this.newlyCreatedItems = this.newItems;
} else {
for ( let i = 0; i < this.newItems.length; i++ ) {
for ( let j = 0; j < this.newlyCreatedItems.length; j++ ) {
if ( this.newItems[i].id == this.newlyCreatedItems[j].id ) {
this.newItems.splice( i, 1 );
}
}
}
}
//THIS IS THE SERVICE I USE TO SEND THE INFO TO THE BACK END
//THIS ONE HAS BEEN IMPORTED FROM AN SERVICE FILE
settingsService.save( this.newItems )
.then(response => {
//WHAT TO DO AFTER THE RESPONSE GOES HERE
});
}
So here's the thing, firing the function for the first time, since it's the first, doesn't duplicate anything in the database... For the second time, it works well and it only saves the new item, from the third time on, it starts duplicating.
If you need me to elaborate more, just let me know, I thank you all in advance...
Quick and dirty using jQuery:
var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
You tagged vue.js but this problem statement is more like from JavaScript side. Basically, You are doing shallow copy of the newItems array into the newlyCreatedItems array which causing the updation issue as both are referencing to the same pointer address.
You can resolve this issue by deep copying with the help of structuredClone() method.
Live Demo :
let newItems = [
{ id: "123", name: "a", otherProps: "someProps" },
{ id: "456", name: "ab", otherProps: "someProps" },
{ id: "789", name: "c", otherProps: "someProps" }
];
let newlyCreatedItems = [];
function saveNewSettings() {
if (!newlyCreatedItems.length ) {
newlyCreatedItems = structuredClone(newItems);
} else {
for ( let i = 0; i < newItems.length; i++ ) {
for ( let j = 0; j < newlyCreatedItems.length; j++ ) {
if ( newItems[i].id == newlyCreatedItems[j].id ) {
newItems.splice( i, 1 );
}
}
}
}
}
saveNewSettings();
console.log(newlyCreatedItems);
console.log(newItems);
console.log('-------');
saveNewSettings();
console.log(newlyCreatedItems);
console.log(newItems);

Create and store different arrays in each object

I'm new programming in JS and I'm designing an application to search for sites by county (it would be something like state) and municipality (Counties).
From the data I obtain through an API I get the following results.
data from API
As you can see there are some counties that are repeated and what I am interested in is to assign the county in an object and within that object create an array with its municipalities.
At the moment I have removed the repeated counties, but I cannot put each object with its municipalities.
API to array
My code is the following basically what I do here is to remove the repeated data from the counties.
(variable data is the values from API picture 1)
for(let i=0; i< data.length; i++)
{
let found = false;
for (var j=0; j<array.length; j++) {
if (data[i].nom_comarca == array[j].comarca) {
municipis.push(data[i].municipi)
found = true;
break;
}
}
if(!found) // If not find we add into Array
{
array.push({
comarca : data[i].nom_comarca,
municipis: municipis
})
//municipis = [];
}
}
Thanks for all really!!
Using a dictionary to keep track of duplicate nom_comarca. Transform the municipi property into an array if the element doesn't yet exist, otherwise push the municipi value to the existing element's array.
const data = [
{"nom_comarca": "1", "municipi": "1"},
{"nom_comarca": "1", "municipi": "2"},
{"nom_comarca": "2", "municipi": "3"},
];
const result = Object.values(data.reduce((acc, el) => {
if (!acc[el.nom_comarca]) {
acc[el.nom_comarca] = {...el, municipi: [el.municipi]};
} else {
acc[el.nom_comarca].municipi.push(el.municipi);
}
return acc;
}, {}));
console.log(result);

for-in loop to for loop or forEach

Ok I have the following scenario. I need to convert this for-in loop to either a for loop or forEach. I have tried a few different examples but can't seem to get the code to append to the page. The for-in loop will work however for the code I need to write, it is not allowed.
This is an example variable
var work = {
"jobs": [{
"employer": "Java",
"title": "Script",
"dates": "2017",
"description": "description",
}
}
This is the code that I have to work. Currently in a for-in loop but need it into a for or forEach loop.
function displayWork() {
for (job in work.jobs) {
//create new div for work experience
$("#workExperience").append(HTMLworkStart);
//concat employer and title
var formattedEmployer = HTMLworkEmployer.replace("%data%",
work.jobs[job].employer);
var formattedTitle = HTMLworkTitle.replace("%data%", work.jobs[job].title);
var formattedEmployerTitle = formattedEmployer + formattedTitle;
$(".work-entry:last").append(formattedEmployerTitle);
var formattedDates = HTMLworkDates.replace("%data%", work.jobs[job].dates);
$(".work-entry:last").append(formattedDates);
var formattedDescription = HTMLworkDescription.replace("%data%",
work.jobs[job].description);
$(".work-entry:last").append(formattedDescription);
});
}
displayWork();
It looks like you will have an array of jobs inside the work object, although your example is missing the closing square bracket for the jobs array, and it might be clearer if you had more than one entry in the array.
Given a data structure like this:
var work = {
jobs: [
{
employer: "example1"
},
{
employer: "example2"
},
{
employer: "example3"
}
]
}
You can use a simple for loop based on the fact that the jobs array will have consecutive integer keys starting at zero:
for (var i = 0; i < work.jobs.length; i++) {
// do stuff with current job in work.jobs[i]
var current_employer = work.jobs[i].employer;
}

How to not proceed with adding a value if its an empty/null string?

My code reads data somewhere and then adds them as values to properties in an object. I made a for loop to iterate over the items in the list so they can each belong in their own cute little object but here is a problem: sometimes there's no value to a property and I wan't to know what I can do to intercept that before it finishes being declared to the property. Maybe replace the empty string with a word or something.
Here is an example code and lets say the title on iteration #3 is going to be an empty "" string. how do I intercept that?
var counter = 0;
for (var i = 0; i < nyData.results.length; i++) {
if (_.indexOf(uniqueItems, nyData.results[i].id)) {
continue;
}
if (!_.indexOf(uniqueItems, nyData.results[i].id)) {
var index = i;
counter++;
let putParams = {
TableName: "Articles",
Item: {
"title": nyData.results[i].title,
"date": nyData.results[i].published_date,
"abstract": nyData.results[i].abstract,
"source": nyData.results[i].source,
"views": nyData.results[i].views,
"author": nyData.results[i].byline,
"section": nyData.results[i].section,
"category": nyData.results[i].des_facet,
"organizations": nyData.results[i].org_facet,
"people_involved": nyData.results[i].per_facet,
"country_subject": nyData.results[i].geo_facet,
"id": nyData.results[i].id,
}
}
db.put(putParams, function(err) {});
}
}
console.log(`Total of ${counter} new articles were inserted into database.`);
callback(null);
if( myVar) {
}
will only be true, if the variable is not:-
Empty
Null
undefined
0
false
....and a few more.

javascript: get index of object from dictonary key

my data array
data : [
{
"name": "Autauga, AL",
"value": 5.6
},
{
"name": "Baldwin, AL",
"value": 5.3
},...
]
How can I retrieve the index of an array object if I just have the name "Autauga, AL"?
I am aware of the brute force loops. is there a better way?
In ECMAScript 5.1+, you can use the Array#filter method to get the actual object:
data.filter(function(item){return item.name == 'Autauga, AL'})[0]
That doesn't get you the index, though. You could do this:
data.map(function(item,index){
return [item, index]
}).filter(function(a){
return a[0].name == 'Autauga, AL'
})[0][1]
Those methods still wind up using loops under the covers, but I guess they look cooler..
For efficient access, you could build an index for the target field:
var dataIndexByName = {}, i, len;
for (i=0, len=data.length; i<len; ++i) {
dataIndexByName[data[i].name] = i
}
After which you can just look for dataIndexByName['Autauga, AL']. That also has the advantage of working in older implementations. It gets a bit more complicated if a given name might show up more than once in the original array, though.
You could do something like this:
for (var i = 0, len = data.length; i++) {
if (data[i].name.indexOf("Autauga, AL") > -1) {
return i;
}
}
You could write a small function to do the job based on Array.prototype.some:
function getIndex(arr, prop, value) {
var idx;
arr.some(function(v, i) {
if (v[prop] == value) {
idx = i;
return true;
}
});
return idx;
}
data = [{"name": "Autauga, AL","value": 5.6},
{"name": "Baldwin, AL","value": 5.3}];
console.log(getIndex(data, 'name', 'Baldwin, AL')); // 1
some is efficient because it stops when the callback first returns true. You may wisht to adjust the condition to suit.

Categories