I am merging two sorted arrays in JavaScript. When I call function with two arrays having numbers it works fine, but when I call that function with strings then it does not work. Why?
function mergeSortedArrays(array1, array2) {
const mergedArray = [];
let array1Item = array1[0];
let array2Item = array2[0];
let i = 1;
let j = 1;
if (array1.length === 0) {
return array2;
}
if (array2.length === 0) {
return array1;
}
while (array1Item || array2Item) {
if (array2Item === undefined || array1Item < array2Item) {
mergedArray.push(array1Item);
array1Item = array1[i];
i++;
} else {
mergedArray.push(array2Item);
array2Item = array2[j];
j++;
}
}
console.log(mergedArray);
}
//working?
mergeSortedArrays([0, 3, 4, 12, 222], [3, 4, 6, 30]);
// not working why?
mergeSortedArrays(["0", "3", "4", "12", "222"], ["3", "4", "6", "30"]);
As said in the comments, strings in JS are compared lexically , so, "222" is smaller than "3".
A solution that I see that you can use, is this one:
After checking the arrays for nullity, then concat then into mergedArray, then use the JS function sort(), with the basic return of value1 - value2, that way it will sort the strings in the order you want, and also will work for numbers.
(Further read: Why is one string greater than the other when comparing strings in JavaScript?)
function mergeSortedArrays(array1, array2) {
let mergedArray = [];
if (array1.length === 0) {
return array2;
}
if (array2.length === 0) {
return array1;
}
mergedArray = array1.concat(array2)
mergedArray.sort(function(a, b) {
return a - b
})
console.log(mergedArray);
return mergedArray;
}
mergeSortedArrays([0, 3, 4, 12, 222], [3, 4, 6, 30]);
mergeSortedArrays(["0", "3", "4", "12", "222"], ["3", "4", "6", "30"]);
BUT, be knowing that this solution will only work as expected if the strings are representations of numbers ("1", "2",...), if it is something like "aa", "abc", "b" it probably won't work well and another solution may be needed. (something like this: https://stackoverflow.com/a/51169/8732818)
Sort for string works differently than numbers. Its based on the ASCII table values. For example "99" > "100000" return should return true
Related
I'm looking for an efficient solution to sort an array depending of how many times an element appears
For example :
let values = ["10", "4", "4", "4", "7", "7"]
I think the best output would be something like [{number, frequency}, ...], which, in the example would look like this :
[{4, 3}, {7, 2}, {10, 1}]
I've seen a lot of ways to do it, but every solution just sort the array depending of the frequency, without any access on how many times the element appears.
At the moment I only have this code that I got from another StackOverflow topic (can't remember which one sorry)
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).sort(function(a, b) {
return map[a] < map[b];
});
console.log(newTypesArray);
It's doing a great job at sorting depending on the frequency, but I can't access how many times an element is repeated. And I have no idea how to do it...
Any thoughts?
const arr = [1, 1, 1, 2, 2, 3];
// use reduce for that.
const result = arr.reduce((result, item) => {
const count = result[item];
if (count === undefined) {
result[item] = 1;
} else {
result[item] += 1;
}
return result;
}, {});
// You'll get the result similar to this: {[item]: [count]}
// And then you can transform it into entries array:
const entries = Object.entries(result);
// entries are [[item, count], ...];
// And then sort
const sorted = entries.sort((entryA, entryB) => entryA[1] - entryB[1]);
// You'll have ascending sorted array by count.
console.log(sorted);
Something like this might do the trick:
const data = ["10", "7", "5", "4", "4", "7", "7", "4", "5", "4"];
const result = data.reduce((r, c) => (r[c] = (r[c] || 0) + 1, r), {});
var keys=Object.keys(result).sort((a,b)=>result[b]-result[a]); // sorted keys
console.log(keys.map(k=>k+':'+result[k])); // output, ordered by keys
It is up to you how you want to return the results. result contains the count and keys the actual values.
You can use Object.entries() over the object you got from reduce now sorting the array of key-value pairs and finally mapping to the array of {number, frequency}:
let values = ["10", "4", "4", "4", "7", "7"]
var map = Object.entries(values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {}))
.sort((a, b) => a[1] < b[1])
.map((a) => ({[a[0]]:a[1]}));
console.log(map);
I think the best output would be something like [{number, frequency}, ...]
Actually these are not valid JavaScript objects inside your output array, if you want to show them as pairs you can use an array of arrays instead of this, the result will be like this:
[[number, frequency], ...]
In your code you were almost there, but you just need to map the result of your first reduce call to get the desired pair:
let values = ["10", "4", "4", "4", "7", "7"];
var map = values.reduce(function(p, c) {
p[c] = (p[c] || 0) + 1;
return p;
}, {});
var newTypesArray = Object.keys(map).map(function(k) {
return [+k, map[k]];
}).sort((a,b) => b[1] - a[1]);
console.log(newTypesArray);
My array contains list of the Arabic and roman numbers as string. I would like to sort them by the order of Roman numbers ascending first then follows the Arabic numbers in ascending order.
I write the code as below,
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
try {
if (Number(a) != null)
a = Number(a);
if (Number(b) != null)
b = Number(b);
} catch (e) {}
if (a > b) {
return 1;
}
if (b > a) {
return -1;
}
if (a == b) {
return a.position - b.position;
}
});
console.log(myarray);
But the results are like,
Results: ii,iii,xv,x,1,2,3,5,v,vi,vii,vi,iv,xix,xiv,58,88,99,101,125,201,601,i
If I have not convert the string to numbers,
Results: 1,101,125,2,201,3,5,58,601,88,99,i,ii,iii,iv,v,vi,vi,vii,x,xiv,xix,xv
My expect result should be
Results: i,ii,iii,iv,v,vi,vi,vii,x,xiv,xv,xix,1, 2, 3,5,58,88,99,101,125,201,601
Multiple things here:
You don't need a try / catch block. Just leave it.
Checking the parsed number for null won't give you the expected result. You need to check for NaN.
Either parse both values to a number or none. Otherwise you can't compare properly.
Usually numbers will be returned before strings. To avoid this behaviour you need to invert the result value by multiplicating with -1, if one value is a number and one is a string.
Here is the working version of the sort method:
var myarray = ["i", "ii", "iii", "xv", "x", "1", "2", "3", "5", "601", "vi", "vii", "88", "99", "201", "101", "xix", "125", "iv", "vi", "v", "xiv", "58"]
myarray.sort(function (a, b) {
if (!isNaN(Number(a)) && !isNaN(Number(b))) {
a = Number(a);
b = Number(b);
}
var result;
if (a > b) {
result = 1;
}
if (b > a) {
result = -1;
}
if (a == b) {
result = a.position - b.position;
}
if (isNaN(Number(a)) !== isNaN(Number(b))) {
result = result * -1;
}
return result;
});
console.log(myarray);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Suppose I have the array as follows
var array=[3,4,5,5,5,6,8,3,12,1,1,1];
Then the result should be
array=[5,1,3,4,6,8,12];
Required implementation in JavaScript or nodejs
I used a combination of lodash and plain JavaScript array methods in this jsbin example:
var arr = [3,4,5,5,5,6,8,3,12,1,1,1];
var sorted = _.sortBy(_.toPairs(arr.reduce(function(agg, curr) {
agg[curr] = agg[curr] ? agg[curr] + 1 : 1;
return agg;
}, {})), function(pair) {
return -pair[1];
}).map(function(pair) {
return pair[0];
});
console.log(sorted); // => ["1", "5", "3", "4", "6", "8", "12"]
However, the ordering of "5" and "1" is different that the ordering of the 3,4,6,8,12, because the sort order wasn't specified for numbers that have identical counts.
What the above does is created a map of number=>count (e.g. { "1": 3, "5": 3 }), and then pairs them as tuples (since objects can't be sorted deterministically in JavaScript: [["1", 3], ["5", 3]]). Then, we simply sort the collection of tuples based on the count, and map over the collection of tuples to return just the number (e.g. ["1", "5", /* etc. */ ]).
var array = [3, 4, 5, 5, 5, 6, 8, 3, 12, 1, 1, 1];
var obj = {};
array.forEach(e => obj[e] = obj[e] + 1 || 1);
var sorted = Object.keys(obj)
.map(e => ({ n: e, times: obj[e] }))
.sort((a, b) => b.times - a.times)
.map(e => e.n);
document.write(sorted);
Here's one way to do it:
JSBIN: https://jsbin.com/josuwir/1/edit?js,console
var array=[3,4,5,5,5,6,8,3,12,1,1,1];
var c = array.reduce(function(a, b) {
a[b] = ++a[b] || 1;
return a;
}, {});
var keys = Object.keys(c);
var nn = keys.sort(function(a, b) {
if (c[a] < c[b]) {
return 1;
} else {
return -1;
}
}).map(function(a) {return Number(a)});
function sortArray(array) {
var reducedArray = array.filter(function(item, pos) { //A copy without duplicates
return array.indexOf(item) == pos;
})
var elementFreq = {} //Object that contains element frequencies
for (var i=0; i<reducedArray.length; i++) {
var count = 0;
for (var j=0; j<array.length; j++) {
if (array[j] == reducedArray[i]) {
count++;
}
}
elementFreq[array[i]] = count;
}
function compare(a,b) { //compares the frequency of two elements
return elementFreq[b]-elementFreq[a]
}
reducedArray.sort(compare) //sorts reducedArray based using compare function
return reducedArray
}
2 quick questions
How do I access the index position in an array using for...of?
How do I access the value in an array using for...in?
Pseudo code
var arr = [3, 5, 7];
var pos, value;
for (pos in arr) {
console.log(pos); // logs "0", "1", "2"
}
for (value of arr) {
console.log(value); // logs "3", "5", "7"
}
There is a way:
for (let [key, value] of arr.entries()) {
// ...
}
It uses the Array.prototype.entries() which returns an iterator over tuples of (key; value) and array destructuring that turns it into 2 separated variables.
And to address your answer in particular: when you iterate over arrays you should use either for (var i = 0; i < arr.length; ++i) or for-of, but not for-in.
You can get the value in for in simply by using the index on the original array:
var arr = [3, 5, 7];
var pos, value;
for (pos in arr) {
console.log(arr[pos]); // logs 3, 5, 7
}
Note that using for...in to iterate arrays is a bad practice.
Getting the index in for…of requires an external counter:
var arr = [3, 5, 7];
var pos = 0, value;
for (value of arr) {
console.log(pos++); // logs 0, 1, 2
}
A better solution for both cases would be Array.prototype.forEach:
arr.forEach((value, index) => {
console.log('index: ', index);
console.log('value: ', value);
});
Using for...in
for (pos in arr) {
console.log(arr[pos]);// logs "3", "5", "7"
}
There is no way using for...of. This will give you an idea.
var arr = ["3", 3, {}, true];
for (value of arr) {
console.log(typeof value);
}
I have object {"5":"5","4":"4","3":"3","2":"2","1":"1","-1":"P1",-2":"P2"}
And use this function to parse elements:
function floormake(inobj) {
var levels = '';
var obj = JSON.parse(inobj);
levels += '<ul>';
Object.keys(obj).sort(-1).forEach(function (key) {
levels += '<li>' + obj[key] + '</li>';
});
levels += '</ul>';
return levels;
}
But result alway sorting by number: -1, -2, 1, 2 etc. BUT i need reverse sorting: 5, 4, 3, 2, 1,
sort(-1) - doesn't work
Consider using .reverse() instead.
Object.keys(obj).sort().reverse().forEach( ....
Reverse documentation
Edit Note: As mentioned by #Shmiddty, the reverse() method does not actually sort. The array will need to be sorted then reversed.
The Array.sort method does not accept an integer as the only optional parameter. It accepts a reference to a function that either returns -1, 0, or 1, and the details can be found here: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort
Here's an example of how to sort based on number:
http://jsfiddle.net/3HFN5/1/
var a = ["5", "9", "1", "399", "23", "21"];
function sorter(b, c) {
return (+b < +c ? 1 : -1);
}
alert(a.sort(sorter));
Or something simpler/better:
http://jsfiddle.net/3HFN5/2/
var a = ["5", "9", "1", "399", "23", "21"];
function sorter(b, c) {
return c - b;
}
alert(a.sort(sorter));
And incorporating this with your actual example:
http://jsfiddle.net/3HFN5/3/
var a = {"2":"2","4":"4","-2":"P2","3":"3","300":"4","1":"1","5":"5","-1":"P1"};
function sorter(b, c) {
return c - b;
}
alert(Object.keys(a).sort(sorter));
I mixed the items in the object around and added one to prove it's sorting accurately/completely.