Javascript ForEach on Array of Arrays - javascript

I am looping through a collection of blog posts to firstly push the username and ID of the blog author to a new array of arrays, and then secondly, count the number of blogs from each author. The code below achieves this; however, in the new array, the username and author ID are no longer separate items in the array, but seem to be concatenated into a single string. I need to retain them as separate items as I need to use both separately; how can I amend the result to achieve this?
var countAuthors = [];
blogAuthors = await Blog.find().populate('authors');
blogAuthors.forEach(function(blogAuthor){
countAuthors.push([blogAuthor.author.username, blogAuthor.author.id]);
})
console.log(countAuthors);
// Outputs as separate array items, as expected:
// [ 'author1', 5d7eed028c298b424b3fb5f1 ],
// [ 'author2', 5dd8aa254d74b30017dbfdd3 ],
var result = {};
countAuthors.forEach(function(x) {
result[x] = (result[x] || 0) + 1;
});
console.log(result);
// Username and author ID become a single string and cannot be accessed as separate array items
// 'author1,5d7eed028c298b424b3fb5f1': 15,
// 'author2,5dd8aa254d74b30017dbfdd3': 2,
Update:
Maybe I can explain a bit further WHY on what to do this. What I am aiming for is a table which displays the blog author's name alongside the number of blogs they have written. However, I also want the author name to link to their profile page, which requires the blogAuthor.author.id to do so. Hence, I need to still be able to access the author username and ID separately after executing the count. Thanks

You could use String.split().
For example:
let result = 'author1,5d7eed028c298b424b3fb5f1'.split(',')
would set result to:
['author1' , '5d7eed028c298b424b3fb5f1']
You can then access them individually like:
result[1] //'5d7eed028c298b424b3fb5f1'

Your issue is that you weren't splitting the x up in the foreach callback, and so the whole array was being converted to a string and being used as the key when inserting into the results object.
You can use array destructuring to split the author name and blog id, and use them to optionally adding a new entry to the result object, and then update that result.
countAuthors = [
['author1', 'bookId1'],
['author2', 'bookId2'],
['author1', 'bookId3'],
['author1', 'bookId4'],
['author2', 'bookId5']
]
var result = {};
countAuthors.forEach(([author, id]) => {
if (result[author] === undefined) {
result[author] = {count: 0, blogIds: []};
}
result[author].count += 1;
result[author].blogIds.push(id);
});
console.log(result);

Related

I need to allocate a url to very student name in Javascript

The name list is supposedly as below:
Rose : 35621548
Jack : 32658495
Lita : 63259547
Seth : 27956431
Cathy: 75821456
Given you have a variable as StudentCode that contains the list above (I think const will do! Like:
const StudentCode = {
[Jack]: [32658495],
[Rose]: [35621548],
[Lita]: [63259547],
[Seth]: [27956431],
[Cathy]:[75821456],
};
)
So here are the questions:
1st: Ho can I define them in URL below:
https://www.mylist.com/student=?StudentCode
So the link for example for Jack will be:
https://www.mylist.com/student=?32658495
The URL is imaginary. Don't click on it please.
2nd: By the way the overall list is above 800 people and I'm planning to save an external .js file to be called within the current code. So tell me about that too. Thanks a million
Given
const StudentCode = {
"Jack": "32658495",
"Rose": "35621548",
"Lita": "63259547",
"Seth": "27956431",
"Cathy": "75821456",
};
You can construct urls like:
const urls = Object.values(StudentCode).map((c) => `https://www.mylist.com?student=${c}`)
// urls: ['https://www.mylist.com?student=32658495', 'https://www.mylist.com?student=35621548', 'https://www.mylist.com?student=63259547', 'https://www.mylist.com?student=27956431', 'https://www.mylist.com?student=75821456']
To get the url for a specific student simply do:
const url = `https://www.mylist.com?student=${StudentCode["Jack"]}`
// url: 'https://www.mylist.com?student=32658495'
Not sure I understand your second question - 800 is a rather low number so will not be any performance issues with it if that is what you are asking?
The properties of the object (after the trailing comma is removed) can be looped through using a for-in loop, (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in)
This gives references to each key of the array and the value held in that key can be referenced using objectName[key], Thus you will loop through your object using something like:
for (key in StudentCode) {
keyString = key; // e.g = "Jack"
keyValue = StudentCode[key]; // e.g. = 32658495
// build the urls and links
}
to build the urls, string template literals will simplify the process (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) allowing you to substitute values in your string. e.g.:
url = `https://www.mylist.com/student=?${StudentCode[key]`}
Note the use of back ticks and ${} for the substitutions.
Lastly, to build active links, create an element and sets its innerHTML property to markup built using further string template literals:
let link = `<a href=${url}>${keyValue}</a>`
These steps are combined in the working snippet here:
const StudentCode = {
Jack: 32658495,
Rose: 35621548,
Lita: 63259547,
Seth: 27956431,
Cathy: 75821456,
};
const studentLinks = [];
for (key in StudentCode) {
let url = `https://www.mylist.com/student=?${StudentCode[key]}`;
console.log(url);
studentLinks.push(`<a href href="url">${key}</a>`)
}
let output= document.createElement('div');
output.innerHTML = studentLinks.join("<br>");
document.body.appendChild(output);

Find a string within a jQuery array that has multiple elements per object

I've got an array of employee's and assigned to each employee are a few elements.
Sample of array below:
var employees = [
{"name":"Johhny Test","salary":"1","email":"abc#123.com"},
{"name":"Mike Milbury","salary":"10","email":"184895#hdsjhfs.com"}
];
I've got a means of gathering the employee's last name and I'm storing it in a variable. I'd like to be able to search for the indexOf the last name housed in this variable so that I know at which position within the array that match is made.
So for instance, this array could be 100 items in size. Ideally I want to know that someone with the last name of "johnson" is in position 50 of this array. That way, I can go in and get the salary and email associated with their record from position 50 in the array.
The code I've got so far is this:
var lname = "Test";
var lnameMatch = employees.indexOf(lname);
console.log(lnameMatch);
This isn't working though - my console is logging -1, suggesting that it doesn't exist in the array. Is there a way that I can specify a element of that array to search against?
Almost like employees(name).indexOf(lname) where it is searching against the name element?
Or am I going about this all wrong and there is perhaps an easier less messy way to accomplish this?
You can use .findIndex:
const employees = [
{"name":"Johhny Test","salary":"1","email":"abc#123.com"},
{"name":"Mike Milbury","salary":"10","email":"184895#hdsjhfs.com"}
];
const lastName = "Test";
const index = employees.findIndex(employee => {
const { name = '' } = employee;
const nameParts = name.split(' ');
const lname = nameParts[nameParts.length-1];
return lastName === lname;
});
console.log(index);
console.log(employees[index]);

How to push object to array in angularjs?

This is my code
$scope.studentDetails=[];
$scope.studentIds={};
$scope.studentIds[0]{"id":"101"}
$scope.studentIds[1]{"id":"102"}
$scope.studentIds[2]{"id":"103"}
in the above code when i select student id:101 i got marks from services like
$scope.studentMarks={};
$scope.studentMarks[0]{"marks":"67"}
$scope.studentMarks[1]{"marks":"34"}
next i select student id:102 i got marks from services like
$scope.studentMarks={};
$scope.studentMarks[0]{"marks":"98"}
$scope.studentMarks[1]{"marks":"85"}
finally i want to store student details in to one array like
$scope.studentDetails=[{"id":"101","marks":[67,34]},{"id":"102","marks":[98,85]}]
using angularjs.
Seems like its more of a JS question than angular.
What about the Javascript push method?
$scope.studentDetails.push({id: 101, marks: [67, 34]});
You can use Array.push to add one object, or concat, to concat array into another array. See the references.
angularJS is just a library to extend Javascript. You push into an array just like you would any object in Javascript.
First off, you need to declare an array.
$scope.studentIds = []; // Array of student ids.
Then when you want to add, you push:
$scope.studentIds.push({id: "101"});
To do this naively you need to loop through the student ids and then loop through the marks object and adding it to your studentDetails object if the ids match:
var studentDetails = [];
for (var id in studentIds) {
var studentDetail = {}; // this will be a single student
var marks = [];
if (studentIds.hasOwnProperty(id)) {
for (var mark in studentMarks) {
if (studentMarks.hasOwnProperty(mark) && mark.id === id) {
studentDetail.id = id;
marks.push(mark.marks);
}
}
studentDetail.marks = marks;
}
studentDetails.push(studentDetail);
}
$scope.studentDetails = studentDetails;

JS and ExpressionEngine - Remove KV pairs by duplicate values only?

We're building a site with ExpressionEngine. We are running a SQL query to gather up all member IDs for a specific member group. After that, we are using EE tags to get data from a custom member field for each member ID.
The ID and field data need to stay paired, as we will be populating a drop-down so that the ID is the value and the field data is the text, so we are currently putting them into a JS array as key/value pairs. The call is as follows:
var array= [
{exp:query sql="SELECT * FROM exp_members WHERE group_id = 5"}
{exp:member:custom_profile_data
member_id="{member_id}"}
{if company != ''}
{{member_id}:"{company}"},
{/if}
{/exp:member:custom_profile_data}
{/exp:query}
};
This gives us the output:
var array = [
{1:"name01"},
{2:"name02"},
{3:"name01"},
{4:"name03"}
];
Now, our problem. We need to remove objects based on duplicate field data (values) only, so the above array would look like this:
var array = [
{1:"name01"},
{2:"name02"},
{4:"name03"}
];
None of these IDs (keys) will ever be the same, but the field data (values) can be. So we want to keep the first KV pair that comes through with a unique value, but remove any subsequent dupes of that value - despite the fact that they will not be true "duplicate values" due to a different ID (key).
Keeping in mind that the KV pairs are all dynamic, is there any possible way to do this via JS so we can create a new array for the cleaned data to pass to the drop-down?
You could handle the duplications by modifying your MySQL query. (In my example, my custom field ID was 1.)
var myArray = [];
{exp:query sql="SELECT MIN(m.member_id) AS co_member_id, d.m_field_id_1 AS company FROM exp_members m INNER JOIN exp_member_data d ON m.member_id = d.member_id WHERE d.m_field_id_1 != '' AND m.group_id > 0 GROUP BY d.m_field_id_1;"}
myArray.push({{co_member_id}: "{company}"});
{/exp:query}
This query would use the first (in the ordinal sense) member_id found; you could also change the MIN to MAX and get the last.
This will give you a clean output in your source, without the need for any additional JS processing. I'd also recommend changing the names of the variables you're outputting as to not conflict in EE's parsing.
I would do it like...
function removeDups(arry){
var tmp = {}, retainIdx=[], newArry=[];
arry.forEach(function(obj, idx){
var val = obj[Object.keys(obj)[0]];
if(val && !tmp[val]){
retainIdx.push(idx);
tmp[val] = true;
}
});
retainIdx.forEach(function(i){
newArry.push(arry[i]);
});
return newArry;
};

IndexedDB Get next unique index based on predicate

Is there a way to retrieve the next unique index in a store based on a predicate on the record. For example if I have a book store full of objects like so:
{name: 'Hello Kitty', author: 'Me', pages: 5}
Would it be possible to return the next unique index on author, but base the uniqueness on the highest number of pages?
index.openKeyCursor('author', IDBCursor.nextunique).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
// How to filter the record by highest number of pages?
cursor.continue();
}
};
This is a bit tricky, but you can do. I will illustrate with my library https://bitbucket.org/ytkyaw/ydn-db but you can use IndexedDB API.
First you have to use compound index (only Firefox and Chrome supported) using array keyPath. Database schema for ydn-db is
var schema = {
stores: [{
name: 'book',
indexes: [{
name: 'author, pages',
keyPath: ['author', 'pages']
}]
}
};
var db = new ydn.db.Storage('db name', schema);
The index, 'author, pages' is sorted by author and then by pages. Then we prepare cursor or create iterator in ydn-db.
var iter = new ydn.db.IndexValueIterator('book', 'author, pages');
By default, order is in ascending. Here we want descending order to get highest pages value. This inadvertently make author to sort in descending order, but there is no way to avoid it.
iter = iter.reverse().unique(); // essentially 'PREV_UNIQUE'
Then, we open the iterator giving rise to cursor with descending ordering. The first cursor is what we want. On next iteration, we skip duplicate author name. This is done by using cursor.continue(next_key) method. next_key is given, such that it won't repeat what already got by giving lowest possible value with known author key.
db.open(function(cursor) {
var book = cursor.getValue();
console.log(book);
var effective_key = cursor.getKey();
var author_key = effective_key[0];
var next_key = [author_key];
return next_key; // continue to this or lower than this key.
}, iter);
Note that, we just need to iterate only unique author and no buffer memory require, and hence scalable.

Categories

Resources