Javascript sort list works only onve - javascript

I'm trying to implement a question like system.
The questions should reorder with every like based on the data attribute 'vote'.
So every time a like event is received from the sockets the sort function gets called.
The first time a question gets liked everything works fine and the list is sorted.
But then it stops sorting. I already checked the data values.
Here is my js function:
function sortQuestionList() {
$('#questionList li').sort(sort_li).appendTo('#questionList');
function sort_li(a, b) {
return ($(b).data('vote')) < ($(a).data('vote')) ? -1 : 1;
}
}

Your sort_li function doesn't return the values sort expects. It should return:
A negative number if a comes before b
A positive number if b comes before a
Zero if they're the same
Your code isn't doing that last thing. This makes sort unable to do its job correctly.
Instead:
function sortQuestionList() {
$("#questionList li").sort(sort_li).appendTo("#questionList");
function sort_li(a, b) {
return $(a).data("vote") - $(b).data("vote"); // ***
}
}
(That assumes that the value of vote is numeric.)

Your sort function works. However, it is not stable. You should write
function sort_li(a, b) {
return $(b).data("vote") - $(a).data("vote");
}
Anyways, this is not your problem.
I made an example here and its working just fine: https://codesandbox.io/s/sort-questions-stackoverlow-49hvi?file=/src/index.js
The code you provided is not the root of your problem.
We cannot provide any further help without a non working example. Please make a minimal pen or codesandbox.
All I can say is: Make sure you call the sort function everytime you update the list items. Make sure no other function overwrites those changes. Make sure you use the right properties ('data-votes' instead of 'votes').

Related

javascript: setting default value with IF statement instead of OR

I'm only sharing a small bit of code because there is so much going on, and I hope this is enough to answer my question.
I have some existing JS where a value is determined with an OR statement and I think I need to convert that to an IF statement. The final output is currently giving me both values if they both exist, and I only want "question" where both "question" and "name" values exist.
var question = new fq.Question(questionData.answerId, topicId,
questionData['question'] || questionData['name'],
questionData['text']);
Instead of using the OR operator (answerData['question'] || answerData['name']), I'd like to do something similar to the following:
if (questionData['question'] is undefined) {
use questionData['question'];
} else {
use instead questionData['name']
}
But, I don't know how I might accomplish such a statement within the () in the existing code pasted above. The name variable/value is always present, so there's no risk in defaulting to that. Question is only defined some of the time. And I don't ever want both appearing.
This is probably outside of the scope of my query here, but to fill in a little more detail, this code eventually outputs JSON files for topics and questions. Topics only have names values, and questions have both names and questions, but I only want the questions json to include questions values, not names. I'm pretty sure this is the key part in all of the JS to determin
Create a function and get value from there.
Need to remember scope of function:
Example Snippet:
var that = this;
var question = new fq.Question(questionData.answerId, topicId,
that.getValue(),
questionData['text']);
function getValue() {
if (questionData['question']) { //null and undefined both are false
return questionData['question']
} else {
return questionData['name']
}
}

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Sorting a HTML structure

Im trying to sort a div structure based on a paramter using a small javscript i found.
It seems to not perform exactly as expected. I understand the sorting function is not parsing the values perfectly...
This is the sorting logic is use...
<script type="text/javascript">
// Sorting Logic
$(function() {
$("a[href*=#]").click(function(e) {
e.preventDefault();
var liContents = [];
$('#ProductPrice span[id*="ABSPrice"]').each(function() {
liContents.push($(this).html());
});
liContents.sort(numOrdDesc);
$('#ProductPrice span[id*="ABSPrice"]').each(function() {
$(this).html(liContents.pop());
});
});
});
function numOrdDesc(a, b) {
return (parseInt(b) - parseInt(a));
}
// End
of Sorting Logic
Since i cannot exactly post the html i am going to add a link to the actual website where you can see this is action. Can anyone point out where i am going wrong?
http://www.absbiz.co.uk/Products/tabid/85/rvdsfcatid/Modems-437/Default.aspx
EDIT: Currently i think the sort is working, however i still cannot move the individual products. Only the prices get sorted and changed....
I took at look at your live site and injected the sorting function you used in your question. I noticed a few things. Firstly, the strings you are passing into your compare function look like this:
"£38.89 ex. VAT"
"£19.93 ex. VAT"
"£44.44 ex. VAT"
...
So parseInt("£38.89 ex. VAT") will return NaN. No compare. We can adjust this to remove all non-decimal information, and also parse a float instead, like so:
parseFloat("£38.89 ex. VAT".replace(/[^0-9\.]/g, "")); // --> 38.89
However, this will sort the above price strings, but we don't have association information on the products to properly sort them in the DOM (yet). For that we need to adjust what you put into your array.
The strategy is to find all the top item containers using $('.ProductListProductItem') and push their html into an array. Sort the array such that the prices are found by $('#ProductPrice span[id*="ABSPrice"]') and stripped of non-decimal information. Each top item container is then repopulated with the html from the sorted array.
Essential code:
var liContents = [];
// Push in all the product containers' innerHTML
$('.ProductListProductItem').each(function () {
liContents.push($(this).html());
});
// Use our new sorting routine
liContents.sort(numOrdDesc);
// Replace the html of each top item container
$('.ProductListProductItem').each(function () {
$(this).html(liContents.pop());
});
// Pass in jQuery elements, not strings
function numOrdDesc($a, $b) {
// Get the price further down in the DOM
var a = $('#ProductPrice span[id*="ABSPrice"]', $a).text();
var b = $('#ProductPrice span[id*="ABSPrice"]', $b).text();
return parseFloat(b.replace(/[^0-9\.]/g, "")) - parseFloat(a.replace(/[^0-9\.]/g, ""));
}
To verify this works, navigate to your live site and open DevTool > Console. Copy the above script and paste it into Console. Hit return, and note that the products are now sorted in ascending order. Enjoy.
Before:
After:
Your parseInt fails because of the pound sign. I'm guessing you want to strip that out in your sort function, and also use parseFloat instead since your prices are floating point numbers.
Try
function numOrdDesc(a, b) {
return parseFloat(b.replace(/[^0-9\.]/g, "")) - parseFloat(a.replace(/[^0-9\.]/g, ""))
}
The replace pretty much removes everything that's not a digit or a dot from the string before attempting to parse it
// "£298.73".replace(/[^0-9\.]/g, "") -> "298.73"
// parseFloat("298.73") -> 298.73
You are missing parameters to the function, you can do it like this.
liContents.sort(function(b, a) {
return (parseInt(b) - parseInt(a));
});

Javascript code causing IE freeze

I have the below code causing Internet Explorer to freeze. It's a project that involves processing student grades as an assignment:
var array1 = StudentGradeAreadHugeList();
var nextArrayItem = function() {
var grade = array1.pop();
if (grade) {
nextArrayItem();
}
};
i hope you can help me with this.
You could show more info about the application you're trying to do. But I believe it's a matter of stack overflow (maybe you're using a big list). So, to overcome that you should modify the "nextArrayItem":
window.setTimeout (nextArrayItem, 0)
The freeze incurring mainly from the big data, but now the Event Loop will handle the Recursion process and not your Call Stack.
This is likely caused by an endless recursion. Be aware of proper handling of return values in IE:
var array1 = StudentGradeAreadHugeList();
var nextArrayItem = function() {
var grade = array1.pop();
if ( grade !== null && typeof(grade) !== "undefined" ) {
nextArrayItem();
}
};
pop() on an empty array will not return boolean false but a typeless "undefined".
There's two problems here:
You might be exceeding the call stack limit
Your if-conditional is set-up incorrectly
For the first issue:
As one of the previous responders mentioned, if you have a very large list you can exceed the limit of the call stack since you need to do a recursive call for each element. While doing setTimeout might work, it feels like a hack-y solution. I think the real issue is that your function is handling the array recursively rather than iteratively. I would recommend re-writing your function using a for-loop.
For the second issue:
Let's say in this case your array was set to [100, 90, 80]. When you invoke nextArrayItem() it will work properly the first two time, but the third time you call nextArrayItem() you are popping off the last remaining item (in this case 100) and your grade will be set to 100 which is a truthy value. Therefore, your if-conditional will pass and your function erroneously try to invoke itself again despite the fact that your array is now empty and the program should now exit the call stack.
I tried testing your code using my example in Chrome and what happens is that it will recurse one too many times and invoke pop on an empty array, which will return undefined.
You can fix this issue by changing the if conditional to check for the last element in the array after you have popped the array.
See revised code:
var nextArrayItem = function() {
var grade = array1.pop();
if (array1[array1.length-1]) {
nextArrayItem();
}
};

Filtering large list with javascript

I have a very large list of elements (14000+), I want to have a search field that as you type text into it, it filters the results and hides unrelated elements.
Currently I'm using this:
$.expr[':'].containsIgnoreCase = function (n, i, m) {
return jQuery(n).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
};
$("#search").on("keyup", function () {
var filter = $("#search").val();
$(".list-group-item").not(":containsIgnoreCase('" + filter + "')").addClass("hidden");
$(".list-group-item:containsIgnoreCase('" + filter + "')").removeClass("hidden");
});
Which works wonderfully...on smaller lists. This list is simply too large to be manageable with that code.
I don't know if there is any other code that can handle this many elements client side. If not, would it be better to leave the list blank, and do an ajax request to populate the list as matches are made?
I think there are many possible ways to optimize the search but whether you decide to use the code as shown in the question or use ajax calls I would suggest as an improvement to reduce the number of calls to the search function by using throttle / debounce. This will prevent the search to be called after each key stroke but instead will delay search execution with a number of milliseconds after the last key stroke. For example the code above can be changed as follows:
function searchFunction () {
var filter = $("#search").val();
$(".list-group-item").not(":containsIgnoreCase('" + filter + "')").addClass("hidden");
$(".list-group-item:containsIgnoreCase('" + filter + "')").removeClass("hidden");
}
$("#search").on("keyup", $.debounce(searchFunction, 300));
There are many open source implementations of debounce function over the net, but in the above example I have used jquery-debounced. In order to see how this works please check this jsfiddle. Another implementation is available in underscore.js library. Also I found a nice article on this subject.

Categories

Resources