I have an array, for simplicity, lets say it is this:
var array = [2,55, 22, 6, 7];
and I want to sort it, I would do this:
array.sort(function (a, b) {
return b > a ? -1: 1;
});
or if I want to sort it ascending I would do this:
array.sort(function (a, b) {
return b < a ? -1: 1;
});
Now, let's say I have a value that I want to sort by.
So basically I want to sort my array by values greater than a number.
so if the number was 6, I would want my array to look something like this:
55, 22, 7, 6, 2
but if I want to sort between two numbers, let's say 6 and 23, I would want it to return something like this:
22, 7, 55, 6, 2
the last 3 items can appear in any order to be honest, but the range I have sorted must appear first.
Does anyone know how I can achieve this.
I have tried like this:
// Sort our mapped array
mapped.sort(function (a, b) {
// Loop through our properties
for (var i = 0; i < fields.length; i++) {
// Get our value (skip the first)
var field = fields[i],
x = a[field.name],
y = b[field.name];
// If our values are the same, go to the next compare
if (x === y)
continue;
// If we are using greater than
if (field.operator === '>') {
// Check that the value matches our expression
return y < field.expression ? -1 : 1;
}
// If we are using less than
if (field.operator === '<') {
// Check that the value matches our expression
return y > field.expression ? -1 : 1;
}
}
});
field.expression holds the range value. As you can see I am doing a loop around my fields and then trying to sort.
Your comparison function will need to check whether an item is in the range and then put that before an item outside of the range. If both are inside, or both are outside, they have to be compared as usual.
The following code does that:
var array = [2,55, 22, 6, 7];
var low = 6,
high = 23;
array.sort(function(a, b) {
return ((b >= low && b < high) - (a >= low && a < high)) || (a - b);
});
This function sorts your array and then pushes all numbers bigger than the given minimum value to the back of the array.
EDIT: Better performance by calling splice() only once.
var array = [2,55, 22, 6, 7];
function sortBetween(arr, min){
var sorted = arr.sort(function(a, b) {
return a - b;
});
var spliceIndex = sorted.length - 1;
while(sorted[spliceIndex] > min) {
spliceIndex--;
}
var tmp = sorted.splice(spliceIndex, sorted.length - 1 - spliceIndex);
tmp.forEach(function(el, i) {
sorted.unshift(el);
});
return sorted;
}
console.log(sortBetween(array, 6));
Result is, that your sorted array starts with your minimum number. There is no need to set the maximum, because you said:
the last 3 items can appear in any order to be honest, but the range I have sorted must appear first.
If an array element falls out of the range you care about, just replace it with an absurdly large value so it moves to the end of the list.
Because a and b have local scope, this won't affect the actual array, just the comparison.
array.sort(function (a, b) {
if (a<6 || a>23) a = Number.MAX_SAFE_INTEGER;
if (b<6 || b>23) b = Number.MAX_SAFE_INTEGER;
return b > a ? -1: 1;
});
I am trying to get this function to split an array into subsets. each subset is to have numbers that are equal to the previous or within 1 from the previous number.
The example I have below should return two subsets but it returns {0, 1, 2, 3} instead. Any idea on what I am doing wrong? Also, is there a better way to dynamically create an array for each new subset? Thanks
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function(a, b){return a-b});
for(var i = 0; i<myarr.length; i++){
var iplus = i+1;
if(i === i || i === iplus){
newArr= [];
newArr.push(i);
}else if (i !== i || i !== iplus){
arr2 =[];
arr2.push(i);
}
}
}
What you are trying to do is usually called "partitioning". The generic version of the problem is to partition an array into sub-arrays using some "rule", or predicate, or condition, which specifies which partition a particular element is supposed to go into, or specifies that it should go into a new partition.
The pseudo code for doing this would be:
To partition an array:
Initialize the resulting array
For each element in the array
If that element starts a new chunk
Create a new empty chunk in the resulting array
Add the element to the most recent chunk
Return the result
This can be expressed in JS quite straightforwardly as
function partition(array, fn) {
return array.reduce((result, elt, i, a) => {
if (!i || !fn(elt, i, a)) result.push([]);
result[result.length - 1].push(elt);
return result;
}, []);
}
Now we need to write the function saying when a new partition should start:
// Is the element within one of the previous element?
function close(e, i, a) {
return Math.abs(e - a[i-1]) > 1;
}
We can now partition the array with
partition([[4, 13, 2, 3], close)
This should work.
function max_tickets() {
var arr = [4, 13, 2, 3];
var myarr = arr.sort(function (a, b) { return a - b });
arrSubsets = [];
arr1 = [];
for (var i = 0; i < myarr.length; i++) {
if (myarr[i - 1] === undefined) {
arr1.push(myarr[i]);
continue;
}
if (myarr[i] - myarr[i - 1] <= 1) {
arr1.push(myarr[i]);
}
else {
arrSubsets.push(arr1);
arr1 = [];
arr1.push(myarr[i]);
}
}
if (arr1.length > 0)
arrSubsets.push(arr1);
}
max_tickets();
Based on your questions:
Any idea on what I am doing wrong?.
Inside of your loop you are using i as if it is the value of the array, but the loop goes from 0 to the value of myarr.length in your particular case 4, so that makes the value of i to be 0, 1, 2, 3.
As you can see you are using the values of the index to compare, instead of using the values of the array in order to use the values of the array you must specify the arrayname[index], in your case myarr[i] that will give you the values: 4, 13, 2, 3.
Also, is there a better way to dynamically create an array for each new subset?
Yes you can create an array inside of another array dynamically inside of a loop:
var b = [];
for(var i = 0; i < 10; i++){
b.push(['I am' + i, i]);
}
As you can see in the previous example I'm creating an array inside of the b array so once the loop finishes the b array will have 10 arrays inside of it with 2 elements each.
I have below string -
var a = "1,2,3,4";
when I do -
var b = a.split(',');
I get b as ["1", "2", "3", "4"]
can I do something to get b as [1, 2, 3, 4] ?
You can use Array.map to convert each element into a number.
var a = "1,2,3,4";
var b = a.split(',').map(function(item) {
return parseInt(item, 10);
});
Check the Docs
Or more elegantly as pointed out by User: thg435
var b = a.split(',').map(Number);
Where Number() would do the rest:check here
Note: For older browsers that don't support map, you can add an implementation yourself like:
Array.prototype.map = Array.prototype.map || function(_x) {
for(var o=[], i=0; i<this.length; i++) {
o[i] = _x(this[i]);
}
return o;
};
My 2 cents for golfers:
b="1,2,3,4".split`,`.map(x=>+x)
backquote is string litteral so we can omit the parenthesis (because of the nature of split function) but it is equivalent to split(','). The string is now an array, we just have to map each value with a function returning the integer of the string so x=>+x (which is even shorter than the Number function (5 chars instead of 6)) is equivalent to :
function(x){return parseInt(x,10)}// version from techfoobar
(x)=>{return parseInt(x)} // lambda are shorter and parseInt default is 10
(x)=>{return +x} // diff. with parseInt in SO but + is better in this case
x=>+x // no multiple args, just 1 function call
I hope it is a bit more clear.
This is very simple.Such as:
["1", "2", "3", "4"].map(i=>Number(i))
you can run the demo.
let result = ["1", "2", "3", "4"].map(i=>Number(i));
console.log(result);
Array.from() for details go to MDN
let a = "1,2,3,4";
let b = Array.from(a.split(','),Number);
or
let c = ["1", "2", "3", "4"].map(Number);
b and c is an array of numbers.
demonstration:
let a = "1,2,3,4";
let b = Array.from(a.split(','),Number);
let c = ["1", "2", "3", "4"].map(Number);
console.log(`b: ${b}, c: ${c}`);
Map it to integers:
a.split(',').map(function(i){
return parseInt(i, 10);
})
map looks at every array item, passes it to the function provided and returns an array with the return values of that function. map isn't available in old browsers, but most libraries like jQuery or underscore include a cross-browser version.
Or, if you prefer loops:
var res = a.split(",");
for (var i=0; i<res.length; i++)
{
res[i] = parseInt(res[i], 10);
}
+string will try to change the string to a number. Then use Array.map function to change every element.
"1,2,3,4".split(',').map(function(el){ return +el;});
A more shorter solution: map and pass the arguments to Number:
var a = "1,2,3,4";
var b = a.split(',');
console.log(b);
var c = b.map(Number);
console.log(c);
One liner
Array.from(a.split(','), Number)
There's no need to use lambdas and/or give radix parameter to parseInt, just use parseFloat or Number instead.
Reasons:
It's working:
var src = "1,2,5,4,3";
var ids = src.split(',').map(parseFloat); // [1, 2, 5, 4, 3]
var obj = {1: ..., 3: ..., 4: ..., 7: ...};
var keys= Object.keys(obj); // ["1", "3", "4", "7"]
var ids = keys.map(parseFloat); // [1, 3, 4, 7]
var arr = ["1", 5, "7", 11];
var ints= arr.map(parseFloat); // [1, 5, 7, 11]
ints[1] === "5" // false
ints[1] === 5 // true
ints[2] === "7" // false
ints[2] === 7 // true
It's shorter.
It's a tiny bit quickier and takes advantage of cache, when parseInt-approach - doesn't:
// execution time measure function
// keep it simple, yeah?
> var f = (function (arr, c, n, m) {
var i,t,m,s=n();
for(i=0;i++<c;)t=arr.map(m);
return n()-s
}).bind(null, "2,4,6,8,0,9,7,5,3,1".split(','), 1000000, Date.now);
> f(Number) // first launch, just warming-up cache
> 3971 // nice =)
> f(Number)
> 3964 // still the same
> f(function(e){return+e})
> 5132 // yup, just little bit slower
> f(function(e){return+e})
> 5112 // second run... and ok.
> f(parseFloat)
> 3727 // little bit quicker than .map(Number)
> f(parseFloat)
> 3737 // all ok
> f(function(e){return parseInt(e,10)})
> 21852 // awww, how adorable...
> f(function(e){return parseInt(e)})
> 22928 // maybe, without '10'?.. nope.
> f(function(e){return parseInt(e)})
> 22769 // second run... and nothing changes.
> f(Number)
> 3873 // and again
> f(parseFloat)
> 3583 // and again
> f(function(e){return+e})
> 4967 // and again
> f(function(e){return parseInt(e,10)})
> 21649 // dammit 'parseInt'! >_<
Notice: In Firefox parseInt works about 4 times faster, but still slower than others. In total: +e < Number < parseFloat < parseInt
As a variant you can use combiantion _.map and _.ary methods from the lodash library. Whole transformation will be a more compact. Here is example from the official documentation:
_.map(['6', '8', '10'], _.ary(parseInt, 1));
// → [6, 8, 10]
Use Array.from for this, Try this:
let b = ["1", "2", "3", "4"];
b = Array.from(b,Number);
console.log(b);
The underscore js way -
var a = "1,2,3,4",
b = a.split(',');
//remove falsy/empty values from array after split
b = _.compact(b);
//then Convert array of string values into Integer
b = _.map(b, Number);
console.log('Log String to Int conversion #b =', b);
Matt Zeunert's version with use arraw function (ES6)
const nums = a.split(',').map(x => parseInt(x, 10));
This works amazing if you need to convert an array of strings to numbers.
const numbers = arr => arr.map(Number);
numbers(['1', '2', '3','4']); // [1, 2, 3, 4]
Since all the answers allow NaN to be included, I thought I'd add that if you want to quickly cast an array of mixed values to numbers you can do.
var a = "1,2,3,4,foo,bar";
var b = a.split(',');
var result = b.map(_=>_|0) // Floors the number (32-bit signed integer) so this wont work if you need all 64 bits.
// or b.map(_=>_||0) if you know your array is just numbers but may include NaN.
You can use JSON.parse, adding brakets to format Array
const a = "1,2,3,4";
const myArray = JSON.parse(`[${a}]`)
console.log(myArray)
console.info('pos 2 = ', myArray[2])
You can transform array of strings to array of numbers in one line:
const arrayOfNumbers = arrayOfStrings.map(e => +e);
let ar = [ '682', '874', '906', '11168', '73714',
'74377', '74034', '138860', '138891', '139161', '139562',
'139733', '139560', '74049', '139759', '139934', '140104',
'141335', '141356', '141334', '141337', '141360', '141358',
'141365', '141419', '143333', '151477', '147342', '141355',
'167847', '192141', '196760', '191687', '197351', '197055',
'198852', '198731', '198816', '199034', '200053', '199226',
'217818', '200055', '222039', '230533', '230530', '231127',
'222042', '231100', '236171', '236913', '236980', '237015',
'237016', '237052', '237551', '237560', '237590', '237637',
'237733', '237731', '237655', '238890', '238910', '238837',
'238926', '238972', '238925', '239755', '239696', '239898',
'240037', '239909', '240036', '240082', '240097', '240526',
'240770', '678151', '678950', '678985'];
let arry=[]
ar.map(arr=>{
arry.push(parseInt(arr))
});
console.log(arry);
What I am trying to do is if I have Array
a = {1,2,3,4};
b = {1,2};
Then I want subset array as c = {3,4};
Can anybody help me?
a = [1, 2, 3, 4]
b = [2, 1, 5]
comp = a.filter(function(e) { return b.indexOf(e) < 0 })
see Array.filter and Array.indexOf for more details and degradation options.
I'm not aware of any built-in way to do this, you basically have to loop through c and check whether each element is in a and, if so, remove it. The Array#indexOf method can help you with checking, but not all implementations have it (though most do). Removal can be via Array#splice.
So:
var a, c, index;
a = [1, 2];
c = [1, 2, 3, 4];
for (index = c.length - 1; index >= 0; --index) {
if (a.indexOf(c[index]) >= 0) {
c.splice(index, 1);
}
}
...and then either supply your own implementation of Array#indexOf if your environment doesn't support it, or use a library like Prototype that supplies it for you (jQuery gives it to you as well, but through its own jQuery.inArray function). If doing it yourself:
if (!Array.prototype.indexOf) {
(function() {
Array.prototype.indexOf = Array_indexOf;
function Array_indexOf(elm) {
var index;
for (index = 0; index < this.length; ++index) {
if (this[index] === elm) {
return index;
}
}
return -1;
}
})();
}
Note that adding to the Array prototype as above can be dangerous when done with poorly-written code code that makes assumptions about the environment. Specifically, code that treats for..in as though it loops through array element indexes (it doesn't, it looks through object property names) will get messed up if you add to the Array prototype. (That's probably why jQuery doesn't do it.)
Live example
Assuming that you're after the relative complement of b in a.
function complement(a, b) {
// convert A to an associative array
var myHash = {};
for (var i = 0; i < a.length; ++i) {
myHash[a[i]] = 1;
}
// remove the elements that exist in B
for (var i = 0; i < b.length; ++i) {
delete myHash[b[i]];
}
// what's left is A \ B
// assumes that no-one broke Array by adding new properties to the prototype
return Object.keys(myHash);
}
// test
var a = [1, 2, 3, 4];
var b = [1, 2];
var c = complement(a, b);
alert(c);
This should scale well for larger arrays, since it uses hash table indexing rather than linear searches to remove the unwanted elements.
Here is another solution which uses Array.filter() and Array.includes()
function arrayDifference(a, b) {
return a.filter((x) => !b.includes(x));
}
const a = [1, 2, 2, 2, 3, 4]
const b = [1, 2]
arrayDifference(a,b) // returns [3, 4]