I have a list of items that are being rendered to the browser and I want to .sort() them in alphabetical order. I've used .sort in other contexts but I'm not sure where exactly I would place it in this one.
In my code I had placed it after .map in order to chain them together, but to no avail so I left it out. Any thoughts?
JS snippet:
import testjson from './test.json';
function loadAllCourses() {
let jsonRes = testjson.d.results.map(function(val) {
return {
"Title": val.Title
}
});
let allTitles = jsonRes;
for (var i = 0; i < allTitles.length; i++) {
$(".all-courses-ul").append("<li>"+allTitles[i].Title+"</li>") // body---to be in abc order
$(".form-control").append("<option>"+allTitles[i].Title+"</option>") // dropdown---same as above
};
} // ------------------ loadAllCourses
loadAllCourses();
You would use:
testjson.d.results.sort(function(a, b){
return (a.Title > b.Title) ? 1 : ((b.Title > a.Title) ? -1 : 0)
}).map(function(val)
Related
why it's giving me the first index and i need the ouput below which is "StOp mAkInG SpOnGeBoB MeMeS!"
function spongeMeme(sentence) {
let x = sentence.split("");
for(let i = 0; i < sentence.length;i+=2){
return x[i].toUpperCase()
}
}
console.log(spongeMeme("stop Making spongebob Memes!")) // S
// output : StOp mAkInG SpOnGeBoB MeMeS!"
You're returning just the first letter of what is stored inside of x.
Try to debug it and see what is going on.
The way I'd do it is like so:
function spongeMeme(sentence) {
return sentence.split("")
.map((s, i) => (i % 2 != 0 ? s.toUpperCase() : s))
.join("");
}
console.log(spongeMeme("stop Making spongebob Memes!"));
Try this:
function spongeMeme(sentence) {
let x = sentence.split("");
let newSentence = '';
for(let i = 0; i < sentence.length;i++){
// If the letter is even
if(i % 2 ==0) {
newSentence += x[i].toUpperCase();
}
// If the letter is odd
else {
newSentence += x[i].toLowerCase();
}
}
return newSentence
}
console.log(spongeMeme("stop Making spongebob Memes!"))
First of all you can only return once per function, so you have to create a variable and store all the new letter inside, and, at the end, return this variable.
You're immediately returning on the first iteration of your for loop, hence the reason you're only getting back a single letter.
Let's start by fixing your existing code:
function spongeMeme(sentence) {
let x = sentence.split("");
for(let i = 0; i < sentence.length; i+=2) {
// You can't return here. Update the value instead.
return x[i].toUpperCase()
}
// Join your sentence back together before returning.
return x.join("");
}
console.log(spongeMeme("stop Making spongebob Memes!"))
That'll work, but we can clean it up. How? By making it more declarative with map.
function spongeMeme(sentence) {
return sentence
.split('') // convert sentence to an array
.map((char, i) => { // update every other value
return (i % 2 === 0) ? char.toUpperCase() : char.toLowerCase();
})
.join(''); // convert back to a string
}
You can also do that:
const spongeMeme = (()=>
{
const UpperLower = [ (l)=>l.toUpperCase(), (l)=>l.toLowerCase() ]
return (sentence) =>
{
let i = -1, s = ''
for (l of sentence) s += UpperLower[i=++i&1](l)
return s
}
})()
console.log(spongeMeme("stop Making spongebob Memes!"))
.as-console-wrapper {max-height: 100% !important;top: 0;}
.as-console-row::after {display: none !important;}
OR
function spongeMemez(sentence)
{
let t = false, s = ''
for (l of sentence) s += (t=!t) ? l.toUpperCase() : l.toLowerCase()
return s
}
but I think this version is slower, because using an index should be faster than a ternary expression
I have the following code
var utils = require(`${__dirname}/../../utils/utils.js`);
...
let object = utils.parse(input);
if (object === undefined){
let helper = utils.recognize(input);
msg.channel.sendMessage("\"" + input + "\" not recognized. Did you mean \"" + helper[0] + "\"?");
object = utils.parse(helper[0]);
}
//code related to object
console.log(object.strLength);
where "parse" tries to match the input to an object in a database, and "recognize" tries to find the best match if the input is spelled incorrectly (Levenshtein) (along with additional info such as how close the match was).
Currently the issue is that the code is ran asynchronously; "object.strLength" returns an undefined before utils.recognize() returns a value. If I copy/paste the recognize() and parse() functions into the file, then the code is run synchronously and I do not run into any issues. However I would rather keep those functions in a separate file as I reuse them in other files.
Is there a way to specify that the functions in utils must be synch? I know that there are libraries that convert asynch into synch but I prefer to use as few libraries as I can help it. I tried to have the recognize functions return a Promise but it ended up as a jumbled mess
edit: here's parse. I did not think it was necessary to answer this question so I did not include it initially:
var db = require(`${__dirname}/../data/database.js`);
...
var parse = (input) => {
let output = db[output];
if (output === null) {
Object.keys(db).forEach((item) => {
if (db[item].num === parseInt(input) || (db[item].color + db[item].type === input)){
output = db[item];
return false;
}
});
}
return output;
}
I solved the issue, thanks everyone. Here's what was wrong, it was with recognize(). It was my mistake to not show the code for it initially.
Original recognize:
var recognize = (item) => {
//iterate through our databases and get a best fit
let bestItem = null;
let bestScore = 99999; //arbitrary large number
//let bestType = null;
//found algorithm online by milot-mirdita
var levenshtein = function(a, b) {
if (a.length == 0) { return b.length; }
if (b.length == 0) { return a.length; }
// swap to save some memory O(min(a,b)) instead of O(a)
if(a.length > b.length) {
let tmp = a;
a = b;
b = tmp;
}
let row = [];
for(let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i-1) == a.charAt(j-1)) {
val = row[j-1]; // match
} else {
val = Math.min(row[j-1] + 1, // substitution
prev + 1, // insertion
row[j] + 1); // deletion
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
//putting this here would make the code work
//console.log("hi");
Object.keys(db).forEach((key) => {
if (levenshtein(item, key) < bestScore) {
bestItem = key;
bestScore = levenshtein(item, key);
}
});
return [bestItem, bestScore];
}
My solution was to move the levenshtein function outside of the recognize function, so if I wanted to I can call levenshtein from another function
#user949300 and #Robert Moskal, I changed the forEach loop into a let...in loop. There is no functional difference (as far as I can tell) but the code does look cleaner.
#Thomas, I fixed the let output = db[output]; issue, oops.
Again, thanks for all of your help, I appreciate it. And happy New Year too
I'm trying to apply a custom sorting comparator to a gulp stream (so I don't have the ability to customize the array). I'm trying to sort everything alphabetically except for a single shared file, which should be sorted to the very top.
Running a simple jsbin test though, I'm seeing the same problem - the file I need at the top isn't sorting correctly.
var files = [
'app/modules/t.css',
'app/shared/dialogs/c.css',
'app/shared/directives/m.css',
'app/shared/scss/app.css',
'app/shared/modals/a.css',
'app/shared/modals/b.css'
];
files.sort(function(file1, file2) {
var sort = 0;
if (file1.indexOf('shared/scss') > -1) {
sort = -1;
} else {
sort = file1.localeCompare(file2);
}
return sort;
});
The resulting output is incorrect, app/shared/scss/app.css has only moved up twice.
"app/modules/t.css"
"app/shared/dialogs/c.css"
"app/shared/directives/m.css"
"app/shared/scss/app.css"
"app/shared/modals/a.css"
"app/shared/modals/b.css"
Here's what I'm expecting:
"app/shared/scss/app.css"
"app/modules/t.css"
"app/shared/dialogs/c.css"
"app/shared/directives/m.css"
"app/shared/modals/a.css"
"app/shared/modals/b.css"
Well, file1 isn't the only variable that could contain app/shared/scss/app.css, there's also file2. In your sort function, you're only checking file1.
So, this sort function should behave the way you intended it to:
files.sort((file1, file2) => {
let sort = 0, substring = "shared/scss";
if (file1.includes(substring)) {
sort = -1;
}
else if (file2.includes(substring)) {
sort = 1;
}
else {
sort = file1.localeCompare(file2);
}
return sort;
});
You're checking if file1 contains shared/scss, but you're not checking if file2 contains it. Add another condition, file2.indexOf('shared/scss') > -1, and if it's true, set sort to 1 to make file2 appear on the top.
var files = [
'app/modules/t.css',
'app/shared/dialogs/c.css',
'app/shared/directives/m.css',
'app/shared/scss/app.css',
'app/shared/modals/a.css',
'app/shared/modals/b.css'
];
files.sort(function(file1, file2) {
var sort = 0;
if (file1.indexOf('shared/scss') > -1) {
sort = -1;
} else if (file2.indexOf('shared/scss') > -1) {
sort = 1;
} else {
sort = file1.localeCompare(file2);
}
return sort;
});
This is a proposal which moves 'shared/scss' to top.
var files = ['app/modules/t.css', 'app/shared/dialogs/c.css', 'app/shared/directives/m.css', 'app/shared/scss/app.css', 'app/shared/modals/a.css', 'app/shared/modals/b.css'];
files.sort(function (a, b) {
var aa = +!a.match(/shared\/scss/),
bb = +!b.match(/shared\/scss/);
return aa - bb || a.localeCompare(b);
});
document.write('<pre> ' + JSON.stringify(files, 0, 4) + '</pre>');
I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};
I know there's been a lot of questions like these around the site, but I'm in a struggle and I have tried to do my homework before asking.
I have an array of objects that each have three fields. Status, Type, and Time. All are integers.
Status is between 1-9 and represents an availability and everything
is sorted by status.
Type represents if the user is 0 - "Paid" or 1 -"Free". And paid are
always above free.
this is my code for that
function sortListings(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
if (typeA == typeB) {
return (statusA < statusB) ? -1 : (statusA > statusB) ? 1 : 0;
} else {
return (typeA < typeB ) ? -1 : 1;
}
}
And this works great. Now sometimes two objects will have the same status and be in the same pay type. So I'd like, in this case, to present the latest time stamp first.
Time is stored as an int ( unix )
I don't know how to go about this. Here is my attempt :
function sortListing(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
if (typeA == typeB) { // has the same type
if (statusA == statusB) { // has the same status
return timeA - timeB; //so sort by time
} else { // different statues, same type
return (statusA < statusB) ? -1 : (statusA > statusB) ? 1 : 0; // sort by status
}
} else {
return (typeA < typeB ) ? -1 : 1;
}
}
As you can see my knowledge of the inner workings of sort is not that great.
Any articles, answers or comments are greatly appreciated.
Your main issue is fall-through to less significant fields if the higher level fields are identical. Try this:
function sortListing(obj1, obj2) {
function compareType(a, b) {
return a.type - b.type;
}
function compareStatus(a, b) {
return a.status - b.status;
}
function compareTime(a, b) {
return a.time - b.time;
}
return compareType(obj1, obj2) ||
compareStatus(obj1, obj2) ||
-compareTime(obj1, obj2); // negative to reverse order
}
The || short circuit operator will cause the second (and subsequently third) comparison to be evaluated only if the prior comparison returns 0. The sort order is trivially changed just by changing the order in which the three functions are called.
The inner functions could, of course, be exposed in a higher level scope, allowing you to use each of those comparator functions individually, or in alternate orders.
Note also how this method avoids dereferencing any of the object properties unless absolutely necessary. If you were sorting thousands of entries that can make a significant difference, although in practise that might be offset by the potential expense of making three function calls internally.... Only benchmarks can really tell.
I would structure the code differently. Compare the most significant key first, then the next most significant, and so on. Only when you've compared all keys and found them all to be equal do you return 0.
function sortListing(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
if (typeA < typeB)
return -1;
if (typeA > typeB)
return 1;
if (statusA < statusB)
return -1;
if (statusA > statusB)
return 1;
if (timeA < timeB)
return -1;
if (timeA > timeB)
return 1;
return 0;
}
Now, any time you see a piece of code that looks like the same thing repeated over and over, a light should go off in your head that something can be generalized:
function compareKeys(k1, k2) {
for (var i = 0; i < k1.length; ++i) {
if (k1[i] < k2[i]) return -1;
if (k1[i] > k2[i]) return 1;
}
return 0;
}
function sortListing(obj1, obj2) {
return compareKeys([obj1.type, obj1.status, obj1.time], [obj2.type, obj2.status, obj2.time]);
}
Another refinement:
function pluck(obj, keynames) {
var keys = [];
for (var i = 0; i < keynames.length; ++i) // could be done with .map()
keys.push(obj[keynames[i]]);
return keys;
}
function sortListing(obj1, obj2) {
var keynames = ["type", "status", "time"];
return compareKeys(pluck(obj1, keynames), pluck(obj2, keynames));
}
I couldn't resist trying out a solution that emphasizes the recursive nature of this problem. You're basically comparing two arrays like this: you compare the first elements and if the first elements are the same then you compare the rest in the same way.
Here is the function to compare the arrays (it's assuming that arrays are the same length):
function compareArray(a1,a2) {
if (a1[0]==a2[0]) {
if (a1.length==1) {
return 0;
}
else {
return compareArray(a1.slice(1),a2.slice(1));
}
}
else {
return (a1[0]<a2[0] ) ? -1 : 1;
}
}
Same function with try/catch to check length:
function compareArray(a1, a2) {
var l = a1.length;
try {
if (l != a2.length) throw "arrays have diff. size";
if (l == 0) throw "empty array";
var h1 = a1[0];
var h2 = a2[0];
if (h1 == h2) {
if (l == 1) {
return 0;
} else {
return compareArray(a1.slice(1), a2.slice(1));
}
} else {
return (h1 < h2) ? -1 : 1;
}
} catch (err) {
// handle err
}
}
Then you can compare the fields
function sortListings(obj1, obj2) {
var statusA = obj1.status;
var statusB = obj2.status;
var typeA = obj1.type;
var typeB = obj2.type;
var timeA = obj1.time;
var timeB = obj2.time;
return compareArray([statusA,typeA,timeA],[statusB,typeB,timeB])
}
Btw you can use the compareArray to compare any number of fields.