Javascript Map and Reduce with Sort [duplicate] - javascript

This simple javascript
var x = new Array();
x[0] = 2.73;
x[1] = 11.17;
x[2] = 3.12
x.sort();
for(var i in x)
alert(x[i]);
produces the results:
11.17, 2.73, 3.12 instead of 2.73, 3.12, 11.17.
Why is that and how can I fix it?
Thanks in advance!

It's sorting alphabetically, try passing your own sorting function:
var x = new Array();
x[0] = 2.73;
x[1] = 11.17;
x[2] = 3.12;
numberSort = function (a,b) {
return a - b;
};
x.sort(numberSort);
for(var i in x) {
alert(x[i]);
}

By default, Array.sort will sort alphabetically (lexographically)...but you can supply your own function. Try:
x.sort(function(a, b) { return a > b ? 1 : -1});

Array.sort() function treats its elements as Strings and if no function is passed to the sort() statement, it converts the elements to Unicode and sorts. Therefore, it is advised to pass a custom sort Function whenever sorting numbers.
function customSort(a, b){
return a - b;
}
console.log([-11,-2, 0 ,100].sort(customSort));
This customSort() function will sort the array in_place in ascending order.

Between them, the existing answers tell you everything, but none of them mention both of the problems in your code. Here's the full answer:
The sort isn't doing what you want because the default sort is lexical (i.e. the array elements are converted to strings and compared alphabetically). You can provide your own comparison function to sort():
x.sort(function(a, b) {
return a - b;
});
Secondly, for...in is actually telling you nothing concrete about whether your array is sorted correctly, because the enumeration of for...in is not defined (even though most but not all browsers do broadly what you'd expect). Use a for loop instead (as indeed you generally should for arrays):
for (var i = 0, len = x.length; i < len; ++i) {
alert(x[i]);
}

You are not iterating properly. It should be:
for (var i = 0; i < x.length; i++) {
alert(x[i]);
}
When you use for..in in javascript this will loop through the properties of the object and the order of iteration is undefined. You should be seeing some strange output as well such as all the functions defined in the Array class.

Related

Adding a vector to another vector without a loop

I have two Javascript vectors, a and b, of the same length. I want to add b to a. The obvious way is:
for (i=0; i<a.length; ++i)
a[i] += b[i]
but this requires a loop. I can do:
a.forEach(function(item,index) {
a[index] += b[index]
})
but this is cumbersome - it requires an unneeded parameter "item". Is there a shorter option? Maybe using some external library?
There is no built-in JS function to do this without looping. Further any library that implements this is going to use looping to do it also.
So, write yourself a short utility function and then just call that function whenever you want to do this. There's going to be looping somewhere in order to implement this as it is not a native JS feature. If you want to "hide" the looping, then just put it in a utility function and just call the function when you need it.
// returns a new array that is the sum of the two vector arrays
function addVectors(a, b) {
return a.map(function(item, index) {
return item += b[index];
});
}
Or, if you want one array modified in place:
// add one vector to another, modifying the first one
function addToVector(a, b) {
a.forEach(function(item, index) {
a[index] += b[index];
});
return a;
}
Or, if the unused item argument bothers you for some reason:
// add one vector to another, modifying the first one
function addToVector(a, b) {
for (var i = 0; i < a.length; i++) {
a[i] += b[i];
}
return a;
}
Note, all of these functions assume a and b are the same length. You would have to specify what you want the behavior to be if they end up not being the same length and you want to check for that. You could throw an exception, just add the parts in common, etc...
For example:
// returns a new array that is the sum of the two vector arrays
function addVectors(a, b) {
if (a.length !== b.length) {
throw new Error("Vector arrays must be the same length to add them");
}
return a.map(function(item, index) {
return item += b[index];
});
}
Are you sure you want to do it without the loop? Ok well:
a.forEach(function(currentElement, Index){
b[Index] += currentElement;
});

JavaScript sort comparator function

Basically I want to build a function which sorts objects in an array by one of the object's properties/member variables. I am preeeety sure that the comparator function is where the error is hidden, but I am not 100% sure.
The output I should get after the sort function is called is 1,2,3. I get 1,3,2 which means that it is unchanged
This is the entire js code (with some comments):
var arr = [];
//object definition and creation
var main = document.getElementById("main");
var task = {
name: "",
priority: 0
};
//first
var one = Object.create(task);
one.priority = 1;
//secondd
var two = Object.create(task)
two.priority = 3;
//last
var three = Object.create(task);
three.priority = 2;
//append
arr.push(one);
arr.push(two);
arr.push(three);
//sort function
function sortT() {
arr.sort(compareFN);
}
//comperator function
function compareFN() {
return task.priority < task.priority;
}
function print() {
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].priority);
}
}
//execution of the program
print();
sortT();
print();
EDIT: The solution is the following - As stated, the comparator function really was the problem, the correct way to write it is the following:
function compareFN(taskA, taskB) {
return taskA.priority < taskB.priority;
}
There are multiple problems with your comparator:
It refers to the global task object instead of the objects being compared.
It compares the object to itself.
It is supposed to perform a three-way comparison.
Try:
var compareFN = function(a, b) {
return a.priority - b.priority;
}
The compare function needs two arguments: the first and the second element it should compare.
So your compareFN should look like this:
function compareFN(taskA, taskB) {
return taskA.priority - taskB.priority;
}
Edit: As NPE said, it is supposed to perform a three-way comparison, so a simple a < b is not so a great idea here.
You need to change the signature of you compare function to include the two tasks.
For ascending order (normally what you want) you need to do b < a, a < b will do descending order
//comperator function
function compareFN(a, b) {
return b.priority < a.priority;
}
The comparator function returns a negative value, zero or a positive value. Those three comprise what is called here the three-way comparison.
So:
'''
function cmp((a, b) => {
// ascending
return a - b
}
'''
For descending return b - a

Simple sort algorithm for names - Javascript

If I have an array of objects Array 1:[Object 1, Object 2, Object 3] and each object looks like the following:
object 1 has properties : creation date, name, class, size where name = Bear
object 2 has properties : creation date, name, class, size where name = Dog
object 3 has properties : creation date, name, class, size where name = Guerilla
And I want to sort these objects based upon their respective names in alphabetical order, so Array 2 looks like the following [Object 1 (Bear), Object 2 (Dog), Object 3 (Guerilla)]
how would I approach this problem in Javascript? Are there any convenience methods for doing this? I am relatively new to JS. Thank you in advance!
Use the sort method with a custom comparer:
Array1.sort(function(x, y){
var n1 = x.name;
var n2 = y.name;
return n1 == n2 ? 0
: n1 < n2 ? -1
: 1;
});
arr.sort(function (a, b) {
return a.localeCompare(b);
});
Note that sort is an in-place sort, so your original array will be sorted.
The javascript sort method accepts an anonymous comparison function that gives you all the flexibility that you would need to sort objects on any attribute.
The general pattern is:
arr.sort(function(a,b){...});
The "anonymous" function provided to the sort method accepts two objects to compare, and should return a value that indicates which element to sort higher, as follows:
if a should be sorted higher than b, return 1
if a should be sorted lower than b, return -1
if a is equivalent to b (for sorting purposes), return 0
Here's an example (ellipses indicate other attributes):
var beasties = [
{name:'Bear', created_at: ... },
{name:'Dog', ... },
{name:'Gorilla', ... }
];
beasties.sort(function(a,b){return a.name > b.name});
Here, I'm relying on the ability for js to compare the strings that are returned by the name attribute.
I think that ought to do the trick for you.
You can pass a custom compare function to Array.sort which will then be used by the sorting algorithm to decide which one of two array elements is larger. When the algorithm compares two objects, it passes these to the compare function. When the function returns 1, it considers the first one as larger. When it returns -1, it considers the second one larger. When it returns 0, it considers them equal.
function compare_function(obj1, obj2){
if (obj1.name == obj2.name) return 0;
if (obj1.name < obj2.name) return -1 else return 1;
}
my_array.sort(compare_function)
You can use the sort method of the array, and pass in a function:
Array1.sort(function(left,right) {
if (left.name < right.name) return -1;
if (left.name > right.name) return 1;
return 0;
});

Count of "Defined" Array Elements

Given following array:
var arr = [undefined, undefined, 2, 5, undefined, undefined];
I'd like to get the count of elements which are defined (i.e.: those which are not undefined). Other than looping through the array, is there a good way to do this?
In recent browser, you can use filter
var size = arr.filter(function(value) { return value !== undefined }).length;
console.log(size);
Another method, if the browser supports indexOf for arrays:
var size = arr.slice(0).sort().indexOf(undefined);
If for absurd you have one-digit-only elements in the array, you could use that dirty trick:
console.log(arr.join("").length);
There are several methods you can use, but at the end we have to see if it's really worthy doing these instead of a loop.
An array length is not the number of elements in a array, it is the highest index + 1. length property will report correct element count only if there are valid elements in consecutive indices.
var a = [];
a[23] = 'foo';
a.length; // 24
Saying that, there is no way to exclude undefined elements from count without using any form of a loop.
No, the only way to know how many elements are not undefined is to loop through and count them. That doesn't mean you have to write the loop, though, just that something, somewhere has to do it. (See #3 below for why I added that caveat.)
How you loop through and count them is up to you. There are lots of ways:
A standard for loop from 0 to arr.length - 1 (inclusive).
A for..in loop provided you take correct safeguards.
Any of several of the new array features from ECMAScript5 (provided you're using a JavaScript engine that supports them, or you've included an ES5 shim, as they're all shim-able), like some, filter, or reduce, passing in an appropriate function. This is handy not only because you don't have to explicitly write the loop, but because using these features gives the JavaScript engine the opportunity to optimize the loop it does internally in various ways. (Whether it actually does will vary on the engine.)
...but it all amounts to looping, either explicitly or (in the case of the new array features) implicitly.
Loop and count in all browsers:
var cnt = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] !== undefined) {
++cnt;
}
}
In modern browsers:
var cnt = 0;
arr.foreach(function(val) {
if (val !== undefined) { ++cnt; }
})
Unfortunately, No. You will you have to go through a loop and count them.
EDIT :
var arrLength = arr.filter(Number);
alert(arrLength);
If the undefined's are implicit then you can do:
var len = 0;
for (var i in arr) { len++ };
undefined's are implicit if you don't set them explicitly
//both are a[0] and a[3] are explicit undefined
var arr = [undefined, 1, 2, undefined];
arr[6] = 3;
//now arr[4] and arr[5] are implicit undefined
delete arr[1]
//now arr[1] is implicit undefined
arr[2] = undefined
//now arr[2] is explicit undefined
Remove the values then check (remove null check here if you want)
const x = A.filter(item => item !== undefined || item !== null).length
With Lodash
const x = _.size(_.filter(A, item => !_.isNil(item)))

Iterate over defined elements of a JS array

I'm using a JS array to Map IDs to actual elements, i.e. a key-value store. I would like to iterate over all elements. I tried several methods, but all have its caveats:
for (var item in map) {...}
Does iterates over all properties of the array, therefore it will include also functions and extensions to Array.prototype. For example someone dropping in the Prototype library in the future will brake existing code.
var length = map.lenth;
for (var i = 0; i < length; i++) {
var item = map[i];
...
}
does work but just like
$.each(map, function(index, item) {...});
They iterate over the whole range of indexes 0..max(id) which has horrible drawbacks:
var x = [];
x[1]=1;
x[10]=10;
$.each(x, function(i,v) {console.log(i+": "+v);});
0: undefined
1: 1
2: undefined
3: undefined
4: undefined
5: undefined
6: undefined
7: undefined
8: undefined
9: undefined
10: 10
Of course my IDs wont resemble a continuous sequence either. Moreover there can be huge gaps between them so skipping undefined in the latter case is unacceptable for performance reasons. How is it possible to safely iterate over only the defined elements of an array (in a way that works in all browsers and IE)?
Use hasOwnProperty within for ... in to make sure that prototype additions aren't included:
for (var item in map)
if (map.hasOwnProperty(item)) {
// do something
}
There are three issues:
You should not use for...in to iterate arrays.
You are using the wrong data type for your requirements.
You are not using for...in correctly.
If you want to have something like a hash table then use a plain object:
var map = {};
map[123] = 'something';
map.foo = 'bar';
// same as map['foo'] = 'bar';
//...
It looks like an array, but it is not. It is an object with property 123. You can use either dot notation obj.key (only if the key is a valid identifier - 123 would not be valid so you have to use the following notation) or array notation obj['key'] to access object properties.
It seems that an object would be a more appropriate data structure.
But even then you should make a call to hasOwnProperty (every time you use for...in):
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
//do something
}
}
This checks whether a property is inherited from the prototype (it will return false then) or is truly an own property.
Use the EcmaScript 5 builtin Object.keys, and on non ES5 browsers, define it thus:
Object.keys = function (o) {
var keys = [];
var hasOwnProp = Object.prototype.hasOwnProperty;
if (Object.prototype.toString.call(o) === '[object Array]') {
for (var k in o) {
if (+k === (k & 0x7fffffff) && hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
keys.sort(keys, function (a, b) { return a - b; });
} else {
for (var k in o) {
if (hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
}
return keys;
};
1) use an object like already suggested, it is by far the best solution.
2) if you for some reason need to use an array - don't be scared looping over it with
for(var i, len = arr.length;len < i;i++)
it's very very fast.
3) don't use $.each or similar methods if you want performance - they create a new callstack for every iteration, which is a huge overhead.
Don't use an array. Use an object hash instead
var map = {};
map[key] = value;
...
for (var key in map) {
do something to map[key]
}
You can't do a lot without actually doing a check to see if the value is undefined and then doing operation a or operation b. It would be better to use a predicate to determine if the value is undefined:
x = $.grep(x, function(v, i) { return (typeof(v) != "undefined"); });
There isn't. The only way would be to omit the items from the collection completely, any solution you come up with would still have to do a test on each element for the value.
You could come up with different methods of adding the items key/value to object literals or what have you, but you would still need to omit undefined entries if you do not wish to enumerate over them.

Categories

Resources