Show only the latest 3 results using a loop in JavaScript - javascript

I have a script that pulls in results and displays it on my site. I want to display only the last three results that are being pulled instead of everything. So the latest results are Test2, Test1 and then Office 365 Planned Service.
Here are all the results being pulled:
Here is my JavaScript code:
$(document).ready(function(){
$pnp.setup({
baseUrl: "https://fh126cloud.sharepoint.com/TrainingResourceCenter/O365Training"
});
$pnp.sp.web.lists.getByTitle("O365RoadMap").items.orderBy("Created", false).get().then(function(items) {
console.log(items);
var result = items.map(item => {
return {
Title: item.Title,
Description: item.Description,
Link: item.Link
}
});
var $table = roadMapDisplay(result);
console.log($table);
$('#title').html($table);
});
function roadMapDisplay(items) {
var table = $('<container/>');
items.forEach(item => {
table.append(`${item.Title}`);
table.append(`<div style="text-indent: 10px">${item.Description.slice(0, -200)}...</div>`);
table.append('<br/>');
});
return table;
}
});
How could I throw a loop onto that function? I've tried so many different ways and have come short.
Thanks all in advance!

The following is specific to arrays. However, it appears there's a $pnp-specific answer which it seems to me should be more efficient.
Assuming your current code works (showing the full list), you can grab just the first three entries in items like this:
items = items.slice(0, 3);
...or the last three like this:
items = items.slice(-3);

I am guessing that $pnp is the PnP JavaScript Library With jQuery In SharePoint based on a quick search online.
I don't know too much about PnP; however, I noticed this documentation in https://github.com/SharePoint/PnP-JS-Core/wiki/Working-With:-Items:
// use odata operators for more efficient queries
$pnp.sp.web.lists.getByTitle("My List").items.select("Title", "Description").top(5).orderBy("Modified", true).get().then((items: any[]) => {
console.log(items);
});
As you can see they are using "top(5)"; therefore, you could change the order, so you get first the last elements, then use top(3) to obtain those three records you wish to display.
Since $pnp seems to follow a chain pattern, I wonder if you could apply again the order method after obtaining the top(3).

To keep two arrays:
var items = [1,2,3,4,5,6]
var n = 3
var remainingArray = items.splice(0, items.length-n);
console.log(remainingArray)//<--first n elements
console.log(items)//<--last n elements

I think your are looking for this. if our query always returns top three items then why we explicitly goes to javascript?
$pnp.sp.web.lists.getByTitle("O365RoadMap").items.top(3).orderBy("Created", false).get().then(function(items) {
console.log(items);
});
you can get more knowledge from here

Related

create one array after using map() twice

I may or may not get in 2 differently formatted bits of data.
They both need to be stripped of characters in different ways. Please excuse the variable names, I will make them better once I have this working.
const cut = flatten.map(obj => {
return obj.file.replace("0:/", "");
});
const removeDots = flatten.map(obj => {
return obj.file.replace("../../uploads/", "");
})
I then need to push the arrays into my mongo database.
let data;
for (const loop of cut) {
data = { name: loop };
product.images.push(data);
}
let moreData;
for (const looptwo of removeDots) {
moreData = {name: looptwo};
product.images.push(moreData);
}
I wanted to know if there is a way to either join them or do an if/else because the result of this is that if I have 2 records, it ends up duplicating and I get 4 records instead of 2. Also, 2 of the records are incorrectly formatted ie: the "0:/ is still present instead of being stripped away.
Ideally I would like have a check that if 0:/ is present, remove it, if ../../uploads/ is present or if both are present, remove both. And then create an array from that to push.
You can do your 2 replace on the same map :
const processed = flatten.map(obj => {
return obj.file.replace("0:/", "").replace("../../uploads/", "");
});
Since you know the possible patterns, you can create a regex and use it to replace any occurrences.
const regex = /(0:\/|(\.\.\/)+uploads\/)/g
const processed = flatten.map(obj => obj.file.replace(regex, ''));
You can verify here
Note, regex is a pattern based approach. So it has pros and cons.
Pro:
You can have any number of folder nesting. Using string ../../uploads/ will restrict you with 2 folder structure only.
You can achieve transformation in 1 operation and code looks clean.
Cons:
Regex can be hard to understand and can reduce readability of code a bit. (Opinionated)
If you have pattern like .../../uploads/bla, this will be parsed to .bla.
Since you ask also about a possible way of joining two arrays, I'll give you couple of solutions (with and w/o joining).
You can either chain .replace on the elements of the array, or you can concat the two arrays in your solution. So, either:
const filtered = flatten.map(obj => {
return obj.file.replace('0:/', '').replace('../../uploads/', '');
});
Or (joining the arrays):
// your two .map calls go here
const joinedArray = cut.concat(removeDots);

Storing data with nested .each() function causes to replicate values

I've modified a survey-multi-choice plugin from JsPsych, in order to get responses in the form of checkboxes, instead of radio-buttons, since I need to present an image to the user, followed by 4 alternatives, like this:
Where A, B, C and D are also images, with their respective checkbox below each one. This structure must be presented more than once, like this:
So in this example, the expected output would be:
{"Q0":["Option A","Option B"]} //this is the first "question" displayed
{"Q1":["Option B","Option C"]} //second one
{"Q2":["Option B","Option D"]} //third one
But instead I get the first answer replicated for the rest of the questions:
{"Q0":["Option A","Option B"]} //this is the first "question" displayed
{"Q1":["Option A","Option B"]} //second one
{"Q2":["Option A","Option B"]} //third one
My code is provided below:
$("div." + plugin_id_name + "-question").each(function(index) {
var id = "Q" + index;
var val = [];
var a = $(".jspsych-survey-multi-choicemrbeta-option");
$("input:checkbox:checked").each(function(){
val.push($(this).attr("value"));
});
var obje = {};
obje[id] = val;
$.extend(question_data, obje);
});
I've tried tracking down the values generated on each step by printing them on console, so I'm guessing the problem is how I'm implementing those nested loops, thus the name of this question.
I've tried different approaches when implementing this loop, without better results:
for (var j = 0; j < trial.options[index].length; j++) {
if ($('jspsych-survey-multi-choicemrbeta-response-' + j).is(':checked')) {
val.push($(this).attr("value"));
}
}
A working example of my full code can be found here, for you to test it (look for the jspsych-survey-multi-choicemrbeta.js file, from line #141). CSS isn't included so it'll look slightly different.
Please note that the output of this code is a CSV file, and the full set of responses is given on a single cell, since all those questions belongs to the same JsPsych "trial".
Thanks for your help.
The inner loop iterates over all checkboxes, not only the ones belonging to the question.
Assuming the checkboxes are descendants of the div for the associated question, you should change the inner loop from this:
$("input:checkbox:checked").each( ...
to this:
$(this).find("input:checkbox:checked").each( ...

Options and value won't display in ember-select-guru

I am trying to use ember-select-guru within my own component. The .hbs element looks like this. {{ember-select-guru multiple=true value=values options=options onSelect=(action "onSelect") and my .js looks like this
values: Ember.computed('user' function() {
const values = [];
this.get('user.listOfThings').forEach(value => {
values.push(value.get('name'));
});
return values;
}),
with a similar thing for the options array.
My problem is that the strings that are in value.get('name') are not displayed. It is definitely picking up the array because the right number of elements appear in the combobox but they are just empty div elements. The variable values ends up being an array of strings which seems to work normally everywhere else. Does someone know something I don't? I cant find anything in the docs or in the source code to help me.
I would also be open to using something else that behaves the same way. The is an example of how it should look in the docs.
I just found the answer. I looked at the source for the docs page and found that the component seems to be looking for an array of objects which have a name field. Luckily enough my list of things already had a name attribute and I just needed to call .toArray(). Code looked like this:
values: Ember.computed('user', function() {
return this.get('user.listOfThings').toArray();
}
However in a more general case you would need to use:
values: Ember.computed('user', function() {
var values = [];
this.get('user.listOfThings').forEach(value => {
values.push({ name: value.get('attributeYouWant'), other: other });
};
return values;
}
This is somewhat useful because an array of strings isn't all that functional but it maybe should be a little clearer than having to look at source code for a web page in the dev tools of your browser.

AngularJS soltuion to determining if any element in array1 exists in array2

I am working on an angularJS widget (my first) and I currently am looking for an angularJS solution to my problem.
Basically I have one array containing a list of string values.
var array1 = [
"Need to Know",
"Test Category 2",
"News"
];
and another array containing another list of string values
var array2 = [
"need to know",
"release notes",
"NEWS"
];
I need a true statement if any element from one array matches any element from the other array. The result also needs to be case insensitive.
Here is my current solution and works great.
angular.module("myWidget", function(...){
// angular code here
})
.service('arrayService', function() {
function arrayToLowerCase(array) {
return array.join("~!~").toLowerCase().split("~!~");
}
function arrayElementIsInArray(array1, array2) {
for (var i in array1) {
if (array2.indexOf(array1[i]) >= 0) {
return true;
}
}
return false;
}
function arrayCompare(array1, array2) {
return arrayElementIsInArray(arrayToLowerCase(array1), arrayToLowerCase(array2));
}
return {
arrayToLowerCase: arrayToLowerCase,
arrayElementIsInArray: arrayElementIsInArray,
arrayCompare: arrayCompare
};
})
the problem is my javascript coders (I primary work in c#) feel there is a more angularJS way to do this but they have brought nothing to the table as a definitive solution. It was suggested that the $filter module might be useful but I didn't see how it would exactly solve my problem.
If I already have the best solution, then awesome. If not please let me know what you think and lets go from there.
Thanks in advance.
EDIT: In response to some of the answers, I felt that I might have misinterpreted my request. What I am asking is there a built in function that angular provides that does this out of the box?
After researching this a bit more; the $filter Module will probably do it with a custom comparater implemented but that seems like way overkill for what I am looking for.
The current responses are all good stuff though. Thanks again!
Absolutely nothing to do with Angular. This is plain data structures and data manipulation. To say there should be a more AngularJS way of doing it would be like saying there should be a more MVC way to add two numbers.
Angular provides no basic data structures and utility set of functions beyond what is available in your browser's native list of array functions, which is different depending on which ECMAScript standard the browser supports.
You may want to look into a library like Lo-Dash for stuff like this (which you can use right along with Angular with no problems) as it's preferable to have proven code for these kind of data manipulations than to constantly have to debug your own.
With Lo-Dash, and remembering the requirement for case-insensitivity:
var array1Lowered = _.map(array1, function (value) { return value.toLowerCase(); });
var anyMatchesBool = _.any(array2, function (value) {
return _.contains(array1Lowered, value);
});
Note that I'm making the assumption that there will be no non-string items in either array.
Lo-Dash normalizes the API so you don't need to worry about what functions each browswer supports. If there's a native function, Lo-Dash will use it because it's faster. If not, Lo-Dash provides an all-JavaScript implementation.
Try this on for size. To me this really has nothing to do with Angular
(function(array1, array2) {
var tlc = function(a) { return a.toLowerCase(); };
array2 = array2.map(tlc);
array1 = array1.map(tlc);
return array1.filter(function(n) {
return array2.indexOf(n) != -1;
}).length > 0;
})(array1, array2);
Using native functions...
var intersection = array1.filter(function(n) {
return array2.indexOf(n) != -1
});
With help from Simplest code for array intersection in javascript

Can this javascript be reduced?

I was curious to see if anyone knew a of a way to reduce this javascript code:
var channels;
channels = [];
$('li.suggestions article').each(function() {
return channels.push($(this).data('channel-id'));
});
It's really simple -- the snippet just initializes an array called "channels", iterates over some DOM elements and collects their "data-channel-id" attribute, adding it to that array.
It is something I do a lot and it would be great to have this snippet simplified further -- I'd accept a CoffeeScript answer too if there is a nice solution.
var channels = $('li.suggestions article').map(function() {
return $(this).data('channel-id');
}).get();

Categories