AngularJS orderBy using lexicographical ordering even with numbers - javascript

I have a value function which is passed into my orderBy as:
function getValue(item){
return [parseInt(item.approx_value_usd) || -1];
}
This definitely always returns a number array, but for some reason on the front-end AngularJS always orders my items by lexicographical order of the property 'approx_value_usd' e.g.
88 > 82 > 8 > 53 (wrong!)
I feel like I'm missing something but can't seem to get anywhere with this problem.

The return value of the "order-by" function is examined using simple comparisons. Your code is returning an array, not just a number. When an array appears in a JavaScript > or < comparison, it'll be converted to a string. That's done by taking the string value of each element in the array and joining them.
Thus, even though you were putting numbers in the array, when Angular actually used the returned value it ends up being a string anyway. If you drop the [ ] it should work.

Related

Pattern Matching in javascript returning true if first array has 2 and second array has 22 How do i solve this?

I have two arrays one of it is having user id and another one is having user ids. Those arrays are as follows.
1)The array which is having user id.
data[key].effective_employees Which is eaqual to [2].
Now I have another array which is having numbers of employee ids which is as follows.
data2[0].id Which is eaqual to [2,22,21].
And now I am trying to see whether the array two has number in array 1 I am using the following logic to see whether it is working or not.
if ((/^\d+$/.test(_.intersection([data2[0].id.toString()], data[key].effective_employees)))) {
let isElem = _.contains(returnStackFilterd, value);
if (isElem == false) {
returnStackFilterd.push(value);
}
} else {
returnStackFilterd = _.without(returnStackFilterd, value);
}
But this is showing true for the number 2 if the array two is having 22. Psudo code of what is happening with it is as follows.
if([2]is in[22,21]){ it is printing true} I want false here as the number two is not in the second array. The second array contains 22 and 21 which is not eaqual to 2
How do i solve this problem? The above psudo code should print false.
Let's break down your test expression and see why it doesn't work.
First off, we know that data[key].effective_employees is [2]. data2[0].id might be [2, 22, 21] or [22, 21]. If I'm understanding your question correctly, you want the whole test expression to return true in the first case and false in the second case.
Rebuilding your test expression from the bottom up, the innermost expression we find is this:
data2[0].id.toString()
This is a string with the value '2,22,21' or '22,21', depending on which case we are talking about. Next, you wrap this string in an array:
[data2[0].id.toString()]
So now we have ['2,22,21'] or ['22,21']. Note the quotes; in either case, it is an array with a single element that is a string.
Next, you take the intersection of this array with data[key].effective_employees, which we know is [2]:
_.intersection([data2[0].id.toString()], data[key].effective_employees)
So this expression is effectively
_.intersection(['2,22,21'], [2])
or
_.intersection(['22,21'], [2])
You are always taking the intersection of two arrays, where the first contains a single string and the second contains a number. They can't have any elements in common, so that's always going to produce an empty array ([]).
Finally, you test whether that empty array matches a regular expression:
/^\d+$/.test(_.intersection([data2[0].id.toString()], data[key].effective_employees))
// is effectively the same as
/^\d+$/.test([])
Regular expressions are supposed to be matched against a string, not an array. JavaScript is very lenient in situations like these and will coerce the value you're passing to a string. That means that the value [] is first converted to the empty string '' before being matched to the regular expression /^\d+$/. The empty string does not contain any digits, so this test always returns false.
This is why your test doesn't work as intended. However, let's take a few steps back, because you seem to be doing many things you don't need to do. Why convert arrays to strings (and then back to array)? Why match against a regular expression, if you just want to know whether two arrays have elements in common?
The following, simpler expression will give you the elements that data[key].effective_employees and data2[0].id have in common:
_.intersection(data[key].effective_employees, data2[0].id)
This will evaluate to either [2] or [], depending on whether data[key].effective_employees contains the number 2 or not.
I suggest saving the result of this expression to a variable, because it makes your code easier to read. Let's call it commonIds:
const commonIds = _.intersection(data[key].effective_employees, data2[0].id)
Now you can formulate different conditions, based on what exactly you want this intersection to be like. My impression is that you just want it to be nonempty (i.e., at least one element in common). In that case, you can compare its length to zero:
if (commonIds.length > 0) {
// code for when there is an overlap
} else {
// code for when there is no overlap
}
As a final note, I recommend assigning your base expressions data[key].effective_employees and data2[0].id to variables as well. Again, this makes your code more readable, and it also ensures that you need to change only one line of code if those base expressions change. Putting it all together:
const key = 'x';
const data = { [key]: {
effective_employees: [2],
}};
const data2 = [{
id: [2, 22, 21],
}];
const userId = data[key].effective_employees;
const employeeIds = data2[0].id;
const commonIds = _.intersection(userId, employeeIds);
if (commonIds.length > 0) {
console.log('userId appears in employeeIds');
} else {
console.log('userId does not appear in employeeIds');
}
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
If data[key].effective_employees is the number 2, and data2[0].id is the array [2, 22, 21], the expression to test whether data2[0].id contains data[key].effective_employees is:
data2[0].id.includes(data[key].effective_employees)
From your original question, data2[0].id.toString() coerces the array to a string 2,22,21, which is no use to you. You also do not need to use Underscore for this.

Please explain the recursive solution of finding the depth of an array

I found solution on this link.
Get array's depth in JavaScript
Due to inadequate reputation, I could not comment to ask for explanation through comments.
function getArrayDepth(value) {
return Array.isArray(value) ?
1 + Math.max(...value.map(getArrayDepth)) :
0;
}
let testRy = [1,2,[3,4,[5,6],7,[8,[9,91]],10],11,12]
console.log(getArrayDepth(testRy))
Could you please explain why you are adding 1 and why you are using the Math.max function and the spread operator in the function above? How does this function work?
First, a primer on recursion:
Recursion ultimately tries to solve the most basic form of the problem you can have and then gradually narrow down any complex problem to the most basic form. So you need the following (probably doesn't make complete sense until you read all of it):
You need to solve the base case.
the base case also serves as a terminal condition. You want to stop calling the function recursively at some point. Once you reach the base case you don't need to recurse any more.
You need a reduction step. You start with a big problem and you aim to go to the base form of it and solve it (the base case from 1.). If the current form is not the base, then it's not solvable - you need to reduce the problem a bit and call the function recursively.
So, in this case, the base case is that you get a value that is not an array. Since it's not an array, it doesn't have depth, hence you return zero. SOLVED! That is it.
However, what happens if you do get an array? Well, any array will have some depth. So, you can count one then get the contents of the array. That's the reduction step - you've reduced it from "I have an array of unknown depth" to "I have the contents of my array". When you recursively call getArrayDepth with the contents, you will re-evaluate whether you have an array or not and count appropriately. If you sum all the times you've had an array with depth of at least 1, you get the depth.
So far, we can solve the following things
Input: 42
Output (depth): 0
Why?: It's the base case - it's not an array, so we return 0 and don't recursively call the function.
Input: [42]
Output (depth): 1
Why?: We have an array ->
   count depth of 1 add the result of the recursive call with the contents 42 -->
   it's the base case - it's not an array, so we return 0 and don't recursively call the function.
Going back, we've had 0 and 1 which totals 1.
Input: [[42]]
Output (depth): 2
Why?: We have an array ->
   count depth of 1 and recursively call with the contents [42] -->
   we have an array -->
       count depth of 1 and recursively call with the contents 42 --->
       it's the base case - it's not an array, so we return 0 and don't recursively call the function.
Going back, we've had 0, 1 and 1 which totals 2.
And so on.
Now, as for why you use Math.max and Array#map. Since arrays can have many elements, you use .map to call getArrayDepth on each of them. This will cause further recursion to work it out but at the end you will get an array of all the depths of the elements, so ["a", ["b"], [["c"]]] will be transformed into [1, 2, 3]. Since you need to get the total depth, you need the highest number - this is achieved by using Math.max with spread syntax to return the maximum value.
The individual values in testRy are:
1
2
[3,4,[5,6],7,[8,[9,91]],10]
11
12
On first execution of getArrayDepth with testRy, Array.isArray(value) will return true. So because this is true, we know we have at least depth 1 so that is why the 1+ is there. It then adds that to the the maximum value of calling getArrayDepth on each element in testRy. So that would look like:
getArrayDepth(1) => Array.isArray() false => 0
getArrayDepth(2) => Array.isArray() false => 0
getArrayDepth([3,4,[5,6],7,[8,[9,91]],10]) => Array.isArray() true => 1 + next max
so we hit another array, now we have at least another depth but we need to check if there are arrays in this array, so the cycle continues with this new array (it still has to finish testRy, but with the way it executes, it will go through this one first):
getArrayDepth(3) => Array.isArray() false => 0
getArrayDepth(4) => Array.isArray() false => 0
getArrayDepth([5,6]) => Array.isArray() true => 1 + next max
and this repeats until the end of testRy.
Translating the code to English would sound something like this:
Is the input an array?
yes - get the largest (Math.max) depth of each of the elements (value.map(getArrayDepth)) and increment it by 1 (1 +) to account for the current input array.
no - there is no array, thus no depth, return 0.
The spread operator is used to supply the array return value of value.map(getArrayDepth) as separate arguments. Since that is what Math.max expects.

$.inArray is giving -1?

This should be pretty straight forward:
HTML
my link
<input type="hidden" id="pageids" value="28,27,26,17,18,19,">
Jquery
var thumbaid = $('#thumba').data("id");
// get position
var itemids = $('#pageids').val();
var itemids_array = itemids.split(',');
var currentpos = $.inArray(thumbaid, itemids_array );
alert(currentpos);
Gives me -1?
The funny thing is that if I replace " $(thumba).data("id")" for a number in the jquery code as "26", it works!
The result should be, in this case, "2".
Any ideas?
You need to convert the value to a string.
var thumbaid = $('#thumba').data("id").toString();
Why...? If you were to
console.log(itemids_array);
you would see this
["28", "27", "26", "17", "18", "19", ""]
They are not numbers, they are strings. See http://api.jquery.com/data/
That is because the thumbaid is a number, and the itemids_array contains strings. Try var currentpos = $.inArray(thumbaid.toString(), itemids_array );
jQuery's data function reads the data- attributes and parses digits to numbers.
jQuery's data function does things to the data it reads from the data-* attributes on initialization, including turning number-like strings into numbers. Since $.inArray does an === check, that's why it fails. You end up looking for the number 26 in an array of strings.
If you simply use .attr("data-id") instead, the conversion won't happen.
This behavior is documented in the data docs:
Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null). A value is only converted to a number if doing so doesn't change the value's representation. For example, "1E02" and "100.000" are equivalent as numbers (numeric value 100) but converting them would alter their representation so they are left as strings. The string value "100" is converted to the number 100.
If you're only using data to read data-* attributes, I recommend using attr instead to avoid this kind of thing, and to avoid the confusion caused by the fact that while data initializes from data-* attributes, it doesn't write to them when you set data. Of course, if you need to store data associated with elements and you don't want it on an attribute (because it's not string data, or you don't want it showing in the DOM inspector), data is the right tool for that job.
The reason its not working, is because jQuery data is returning an int not string.
See this example.
my link
<input type="hidden" id="pageids" value="28,27,26,17,18,19,">
Javascript:
var thumbaid = $('#thumba').data('id').toString();
// get position
var itemids = $('#pageids').val();
var itemids_array = itemids.split(',');
console.log(itemids_array);
var currentpos = $.inArray(thumbaid, itemids_array );
console.log(currentpos);
$().data() will convert the data-* attribute's value to a JavaScript value. In your case, thumbaid is converted to a number.
$.inArray compares elements using the strict equality operator(===). That is, '26' === 26 returns false as no type coercion occurs.
From the jQuery.data() docs:
Every attempt is made to convert the string to a JavaScript value
(this includes booleans, numbers, objects, arrays, and null). A value
is only converted to a number if doing so doesn't change the value's
representation. For example, "1E02" and "100.000" are equivalent as
numbers (numeric value 100) but converting them would alter their
representation so they are left as strings. The string value "100" is
converted to the number 100.

javascript sort function sorting wrong [duplicate]

This question already has answers here:
How to sort an array of integers correctly
(32 answers)
Closed 9 years ago.
Hello I have a textbox having values like
<input type="hidden" value="2,1,4,5,3,6,7,8,9,10,11,12" class="sortvalues" id="1_1_parent">
Now what I want to take the value of this textbox, want to split the values to array and then as last result I need a sorted array.
What I have done.
allsortedValues = $(".sortvalues").val();
allsortedValues = allsortedValues.split(",");
allsortedValues = allsortedValues.sort();
When I check the array
console.log(allsortedValues);
It shows
1,10,11,12,2,3,4,5,6,7,8,9
Sorting array as 1, 10, 11, 12, 2.....
I have even used
allsortedValues = allsortedValues.split(",").map(function(x){return parseInt(x)});
before applying sort and in other case I have even used parseInt like
for(var i = 0; i < allsortedValues.length; i++) {
allsortedValues[i] = parseInt(allsortedValues[i]);
}
before applying sort but in all cases result is same. Will some one guide what am I doing wrong?
You'll have to pass in a comparator function that converts the strings to numbers:
allsortedvalues = allsortedvalues.sort(function(a,b) {
return (+a) - (+b);
});
If there's a chance that some of your array entries aren't nicely-formatted numbers, then your comparator would have to get more complicated.
The construction (+a) involves the unary + operator, which doesn't do anything if a is already a number. However if a is not a number, the result of +a will be either the value of a when interpreted as a number, or else NaN. A string is interpreted as a number in the obvious way, by being examined and parsed as a string representation of a number. A boolean value would be converted as false -> 0 and true -> 1. The value null becomes 0, and undefined is NaN. Finally, an object reference is interpreted as a number via a call to its valueOf() function, or else NaN if that doesn't help.
It's equivalent to use the Number constructor, as in Number(a), if you like. It does exactly the same thing as +a. I'm a lazy typist.
If compareFunction is not supplied, elements are sorted by converting
them to strings and comparing strings in lexicographic ("dictionary"
or "telephone book," not numerical) order. For example, "80" comes
before "9" in lexicographic order, but in a numeric sort 9 comes
before 80.
To compare numbers instead of strings, the compare function can simply subtract b from a:
function compareNumbers(a, b)
{
return a - b;
}
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort

Sorting javascript arrays by [field] values gives wrong order

I'm implementing SlickGrid and I want to allow sorting of values by columns, which is supported by SlickGrid by sorting arrays, however, when I sort the columns (arrays) they are not put in the "correct" order.
The order in which they are returned is 1,10,100,11,199,2,20,200,3,30,300....
The problem is displayed very clearly when trying to sort the tasks in this grid by title:
http://mleibman.github.com/SlickGrid/examples/example-multi-column-sort.html
Although I use my own sorting rule, instead of the one used in the example:
data.sort(function(a, b){
var result =
a[field] === b[field] ? 0 :
a[field] > b[field] ? 1 : -1
;
return args.sortAsc ? result : -result;
});
The problem persists.
My question is merely how to sort the array, so that the title (and other data) will be displayed in the correct order: 1,2,3,100,200,300...
Your numbers are actually strings and will be compared as such. To prevent this, use parseInt(a[field],10) > parseInt(b[field],10)
if you are just trying to sort objects of the same type, then there is no problem... your algorithm seems right.
but it seems that your numbers might be actually strings, so they are sorted as strings.
you can parse them into numbers .. ie parseInt() , parseFloat() etc.
But if the field is a string followed by a number like the example in the link, then it will not work
"data 20" will always be less than "data 3", so you might want to extract the number then sort by both the string and the number.

Categories