Related
I was wondering how I'd go about implementing a method in javascript that removes all elements of an array that clear a certain condition. (Preferably without using jQuery)
Ex.
ar = [ 1, 2, 3, 4 ];
ar.removeIf( function(item, idx) {
return item > 3;
});
The above would go through each item in the array and remove all those that return true for the condition (in the example, item > 3).
I'm just starting out in javascript and was wondering if anyone knew of a short efficient way to get this done.
--update--
It would also be great if the condition could work on object properties as well.
Ex.
ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
return item.str == "c";
});
Where the item would be removed if item.str == "c"
--update2--
It would be nice if index conditions could work as well.
Ex.
ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
return idx == 2;
});
You can use Array filter method.
The code would look like this:
ar = [1, 2, 3, 4];
ar = ar.filter(item => !(item > 3));
console.log(ar) // [1, 2, 3]
You could add your own method to Array that does something similar, if filter does not work for you.
Array.prototype.removeIf = function(callback) {
var i = 0;
while (i < this.length) {
if (callback(this[i], i)) {
this.splice(i, 1);
}
else {
++i;
}
}
};
To me, that's one of the coolest features of JavaScript. Ian pointed out a more efficient way to do the same thing. Considering that it's JavaScript, every bit helps:
Array.prototype.removeIf = function(callback) {
var i = this.length;
while (i--) {
if (callback(this[i], i)) {
this.splice(i, 1);
}
}
};
This avoids the need to even worry about the updating length or catching the next item, as you work your way left rather than right.
You can use Array.filter(), which does the opposite:
ar.filter(function(item, idx) {
return item <= 3;
});
You can use lodash.remove
var array = [1, 2, 3, 4];
var evens = _.remove(array, function(n) {
return n % 2 == 0;
});
console.log(array);
// => [1, 3]
console.log(evens);
// => [2, 4]
Make it a one-liner with arrow function:
ar = ar.filter(i => i > 3);
simply write the following example if condition could work on object properties as well
var ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
var newArray = [];
for (var i = 0, len = ar.length; i<len; i++) {
if (ar[i].str == "b")
{newArray.push(ar[i]);};
};
console.log(newArray);
See the example Live Example
if you need to remove exactly one item, and you know for sure that the item exists, you can use this one-liner:
ar.splice(ar.findIndex(el => el.id === ID_TO_REMOVE), 1);
// or with custom method:
let ar = [ {id:1, str:"a"}, {id:2, str:"b"}, {id:3, str:"c"}, {id:4,str:"d"} ];
ar.removeById = id => ar.splice(ar.findIndex(el => el.id === id), 1);
ar.removeById(ID_TO_REMOVE);
http://jsfiddle.net/oriadam/72kgprw5/
ES6 only
I love these kinds of questions and just a different version from me too... :)
Array.prototype.removeIf = function(expression) {
var res = [];
for(var idx=0; idx<this.length; idx++)
{
var currentItem = this[idx];
if(!expression(currentItem))
{
res.push(currentItem);
}
}
return res;
}
ar = [ 1, 2, 3, 4 ];
var result = ar.removeIf(expCallBack);
console.log(result);
function expCallBack(item)
{
return item > 3;
}
My solution for an array of numbers would be:
ar = ar.filter(item => item < 4);
For the in-place remove, my solution is
ar.filter(item => !(item > 3))
.forEach(obsoleteItem => ar.splice(ar.indexOf(obsoleteItem), 1));
Incorrect way
First of all, any answer that suggests to use filter does not actually remove the item. Here is a quick test:
var numbers = [1, 2, 2, 3];
numbers.filter(x => x === 2);
console.log(numbers.length);
In the above, the numbers array will stay intact (nothing will be removed). The filter method returns a new array with all the elements that satisfy the condition x === 2 but the original array is left intact.
Sure you can do this:
var numbers = [1, 2, 2, 3];
numbers = numbers.filter(x => x === 2);
console.log(numbers.length);
But that is simply assigning a new array to numbers.
Correct way to remove items from array
One of the correct ways, there are more than 1, is to do it as following. Please keep in mind, the example here intentionally has duplicated items so the removal of duplicates can be taken into consideration.
var numbers = [1, 2, 2, 3];
// Find all items you wish to remove
// If array has objects, then change condition to x.someProperty === someValue
var numbersToRemove = numbers.filter(x => x === 2);
// Now remove them
numbersToRemove.forEach(x => numbers.splice(numbers.findIndex(n => n === x), 1));
// Now check (this is obviously just to test)
console.log(numbers.length);
console.log(numbers);
Now you will notice length returns 2 indicating only numbers 1 and 3 are remaining in the array.
i have a problem and i need help for this question.
My reverse function doesn't work the way I want it to.
function reverseArrayInPlace(array){
let old = array;
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
};
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
I expect on [5, 4, 3, 2, 1] but i have [5, 4, 3, 4, 5].
Why does it work this way? Please help me understand.
P.S I know about the reverse method.
Variable, with assigned object (array, which is a modified object in fact) - stores just a link to that object, but not the actual object. So, let old = array; here you just created a new link to the same array. Any changes with both variables will cause the change of array.
(demo)
let arr = [0,0,0,0,0];
let bubu = arr;
bubu[0] = 999
console.log( arr );
The simplest way to create an array clone:
function reverseArrayInPlace(array){
let old = array.slice(0); // <<<
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
P.s. just for fun:
function reverseArrayInPlace(array){
let len = array.length;
let half = (len / 2) ^ 0; // XOR with 0 <==> Math.trunc()
for( let i = 0; i < half; i++ ){
[ array[i], array[len - i-1] ] = [ array[len - i-1], array[i] ]
}
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
If you're writing a true in place algorithm, it's wasteful from both a speed and memory standpoint to make an unnecessary copy (as other answers point out--array and old are aliases in the original code).
A better approach is to iterate over half of the array, swapping each element with its length - 1 - i compliment. This has 1/4 of the iterations of the slice approach, is more intuitive and uses constant time memory.
const reverseArrayInPlace = a => {
for (let i = 0; i < a.length / 2; i++) {
[a[i], a[a.length-1-i]] = [a[a.length-1-i], a[i]];
}
};
const a = [1, 2, 3, 4, 5];
reverseArrayInPlace(a);
console.log(a);
Since this is a hot loop, making two array objects on the heap just to toss them out is inefficient, so if you're not transpiling this, you might want to use a traditional swap with a temporary variable.
function reverseArrayInPlace(a) {
for (var i = 0; i < a.length / 2; i++) {
var temp = a[i];
a[i] = a[a.length-i-1];
a[a.length-i-1] = temp;
}
};
var a = [1, 2, 3, 4];
reverseArrayInPlace(a);
console.log(a);
You have in old variable the same array (not by value, but by reference), so if you change any of them you'll have changes in both.
So you should create new array, make whatever you want with them and return it (or just copy values back to your origin array if you don't want to return anything from your function).
It happend like that because:
1) arr[0] = arr[4] (5,2,3,4,5)
2) arr[1] = arr[3] (5,4,3,4,5)
....
n) arr[n] = arr[0] (5,4,3,4,5)
So you can just make a copy (let old = array.slice()) and it'll be woking as you'd expect.
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.
var Arr1 = [1,3,4,5,6];
var Arr2 = [4,5,6,8,9,10];
I am trying to do merge these two arrays and output coming is [1,3,4,5,6,4,5,6]
I have used $.merge(Arr1, Arr2); this piece to merge them. Using alert I can see the merged array like above.
Now my question is how can I get the following output:
[1,3,4,5,6,8,9,10]
i.e. the elements should be unique as well as sorted in the same manner I have mentioned.
Please help.
You can use Array.prototype.sort() to do a real numeric sort and use Array.prototype.filter() to only return the unique elements.
You can wrap it into a helper similar to this:
var concatArraysUniqueWithSort = function (thisArray, otherArray) {
var newArray = thisArray.concat(otherArray).sort(function (a, b) {
return a > b ? 1 : a < b ? -1 : 0;
});
return newArray.filter(function (item, index) {
return newArray.indexOf(item) === index;
});
};
Note that the custom sort function works with numeric elements only, so if you want to use it for strings or mix strings with numbers you have to update it off course to take those scenarios into account, though the rest should not change much.
Use it like this:
var arr1 = [1, 3, 4, 5, 6];
var arr2 = [4, 5, 6, 8, 9, 10];
var arrAll = concatArraysUniqueWithSort(arr1, arr2);
arrAll will now be [1, 3, 4, 5, 6, 8, 9, 10]
DEMO - concatenate 2 arrays, sort and remove duplicates
There is many ways of doing this I'm sure. This was just the most concise I could think off.
merge two or more arrays + remove duplicities + sort()
jQuery.unique([].concat.apply([],[[1,2,3,4],[1,2,3,4,5,6],[3,4,5,6,7,8]])).sort();
One line solution using just javascript.
var Arr1 = [1,3,4,5,6];
var Arr2 = [4,5,6,8,9,10];
const sortedUnion = [... new Set([...Arr1,... Arr2].sort((a,b)=> a-b))]
console.log(sortedUnion)
This looks like a job for Array.prototype.indexOf
var arr3 = arr1.slice(), // clone arr1 so no side-effects
i; // var i so it 's not global
for (i = 0; i < arr2.length; ++i) // loop over arr2
if (arr1.indexOf(arr2[i]) === -1) // see if item from arr2 is in arr1 or not
arr3.push(arr2[i]); // it's not, add it to arr3
arr3.sort(function (a, b) {return a - b;});
arr3; // [1, 3, 4, 5, 6, 8, 9, 10]
a = [1, 2, 3]
b = [2, 3, 4]
$.unique($.merge(a, b)).sort(function(a,b){return a-b}); -> [1, 2, 3, 4]
Update:
This is a bad idea, since the 'unique' function is not meant for use on numbers or strings.
However, if you must then the sort function needs to be told to use a new comparator since by default it sorts lexicographically.
Using underscore.js:
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]).sort(function(a,b){return a-b});
=> [1, 2, 3, 10, 101]
This example is taken directly from underscore.js, a popular JS library which complements jQuery
I did that as follows, where t1 and t2 are my two tables.
The first command put the values of the table t2 to the t1. The second command removes the duplicate values from the table.
$.merge(t1, t2);
$.unique(t1);
function sortUnique(matrix) {
if(matrix.length < 1 || matrix[0].length < 1) return [];
const result = [];
let temp, ele;
while(matrix.length > 0) {
temp = 0;
for(let j=0; j<matrix.length; j++) {
if(matrix[j][0] < matrix[temp][0]) temp = j;
}
if(result.length === 0 || matrix[temp][0] > result[result.length-1]) {
result.push(matrix[temp].splice(0,1)[0]);
} else {
matrix[temp].splice(0,1);
}
if(matrix[temp].length===0) matrix.splice(temp, 1);
}
return result;
}
console.log(sortUnique([[1,4,8], [2,4,9], [1,2,7]]))
Using JavaScript ES6 makes it easier and cleaner. Try this:
return [...Arr1, ...Arr2].filter((v,i,s) => s.indexOf(v) === i).sort((a,b)=> a - b);
and there you have it. You could build it in a function like:
function mergeUniqueSort(Arr1, Arr2){
return [...Arr1, ...Arr2].filter((v,i,s) => s.indexOf(v) === i).sort((a,b)=> a - b);
}
and that settles it. You can also break it down using ES6. Use a Spread Operator to combine arrays:
let combinedArrays = [...Arr1, ...Arr2]
then get the unique elements using the filter function:
let uniqueValues = combinedArrays.filter((value, index, self ) => self.indexOf(value) === index)
Lastly you now sort the uniqueValue object:
let sortAscending = uniqueValues.sort((a-b) => a - b) // 1, 2, 3, ....10
let sortDescending = uniqueValues.sort((b-a) => b - a) // 10, 9, 8, ....1
So you could use any part, just in case.
I wanted to change the rows into columns of an array.
[
[1],
[1,2],
[1,2,3],
[4,2,3],
[4,5,3],
[4,5,6]
]
to
[
[1,1,1,4,4,4],
[2,2,2,5,5],
[3,3,3,6]
]
I tried
var res = [];
for(i in this.fields) {
for(j in this.fields[i].value) {
if(i === 0) res[j] = [];
res[j][i] = this.fields[i].value[j];
}
}
this gives me empty set.
Create this function:
function transpose(arr) {
return Object.keys(arr[0]).map(function (c) {
return arr.map(function (r) {
return r[c];
});
});
}
and then:
var transposedArray = transpose(originalArray);
What you're asking looks a little weird because you have different lengths and you're ignoring undefined values, but it is still achievable.
Don't use for..in loops for Array, use a normal for. Also, you'll need to know how many items you'll have in your new parent Array, which is the max of the lengths of the original child Arrays.
var arrR = [ // will refer to "down" and "across" as in this literal
[1],
[1, 2],
[1, 2, 3],
[4, 2, 3],
[4, 5, 3],
[4, 5, 6]
];
function r2c(arr) {
var arrC = [], // next get the longest sub-array length
x = Math.max.apply(Math, arr.map(function (e) {return e.length;})),
y = arr.length,
i, j;
for (i = 0; i < x; ++i) { // this is the loop "down"
arrC[i] = [];
for (j = 0; j < y; ++j) // and this is the loop "across"
if (i in arr[j])
arrC[i].push(arr[j][i]);
}
return arrC;
}
var arrC = r2c(arrR);
/* [
[1, 1, 1, 4, 4, 4],
[2, 2, 2, 5, 5],
[3, 3, 3, 6]
] */
You should still consider if you're happy with [[1], [1, 2], [1]] becoming [[1, 1, 1], [2]], which I would consider unexpected (the position of 2 is completely lost), but seems to be what you intend.
Similar to Pauls but doesn't need to get the max length first:
function transpose(arr) {
// Loop over arrays as long as one has values
// Arrays should be contiguous, may fail if sparse
for (var result = [], i=0, more; more; i++) {
more = false;
// Get the ith element of each array (if there is one)
for (var j=0, jLen=arr.length; j<jLen; j++) {
// Don't add missing members
if (arr[j].hasOwnProperty(i)) {
// Add array for result if not already there
result[i] = result[i] || [];
// Do transpose
result[i][j] = arr[j][i];
// Only keep going while there is data
more = true;
}
}
}
return result;
}
BTW, a fixed version of your original function is:
function transpose2(fields) {
// Make sure the result array is initialised
var res = [];
// Don't forget to keep counters local - declare them
// I've removed *this* as it's a plain function, use it if
// it's an instance method
for(var i in fields) {
// Values are read directly, there is no "value" accessor
for(var j in fields[i]) {
// Don't rely on order of enumeration - may not start at 0
if(!res[j]) res[j] = [];
// Do the transpose
res[j][i] = fields[i][j];
}
}
return res;
}
But as noted above, for..in is not liked for arrays, particularly as there are many libraries that extend built-ins like Array.prototype so you will traverse those properties too. But if you're cool with that, this is a good way to deal with sparse arrays. You can add a hasOwnProperty test to avoid inherited enumerables.
Note also that the order of enumeration isn't necessarily from '0' or in any particular order, hence changed way of initialising res[j].