Remove Duplicate rows in multidimensional array using Javascript - javascript

var MyArray = [
[1, "07/28/2014"],
[2, "07/29/2014"],
[3, "07/28/2014"],
[4, "07/30/2014"],
[5, "07/28/2014"],
];
In this array, how to remove the duplicate columns and find count the removed items.
I need individual count for removed items.
like
07/28/2014 is 2 times.
This is my Code:
function(MyArray, search_val)
{
var counter = 0; alert(MyArray.length);
for(var i = 0; i < MyArray.length; i++)
{
if(MyArray[i][0] == search_val)
{
MyArray.splice(i, 1);
counter++;
}
}
alert("counter: "+counter);
return counter;
}

Here are a couple very naive basic solutions. Both of these examples are for demonstration purposes only. Do not use in production code. There are parameter validation and other checks that were left out to simplify the examples. Further, depending on your browser requirements, or available frameworks, there are much faster and better ways to do your equality tests. Think of this as a starting point for more research.
I'll leave it as an exercise to improve these answers.
For your data exclusively, this would work:
var count, isFirst, data = [[1,"07/28/2014"], [2,"07/29/2014"],[3, "07/28/2014"],[1,"07/28/2014"],[4, "07/30/2014"]];
count = 0
/* loop over each element of the array */
for(var x = 0; x < data.length; x++) {
isFirst = true // used to skip the first matching element
/* for each loop iteration, loop over every element in the array */
for(var y = 0; y < data.length; y++) {
/*
check the inner loop element against the outer loop element
to see if they satisfy your equality requirements.
Notice the second set of index operator brackets, this is
how you access the next dimension of the array.
*/
if(data[x][1] === data[y][1]) {
/*
If this is not the first time we've found this match
then this must be a duplicate element, so remove it
*/
if (!isFirst) {
data.splice(y, 1);
count++;
}
isFirst = false // makes sure that future matches are removed
}
}
}
console.log(count);
console.log(data);
For a more general solution one possibility would be to pass in the equality test as an anonymous function:
/* Note, this is the same algorithm as above */
function spliceIf(data, predicate) {
var count = 0, isFirst;
for(var x = 0; x < data.length; x++) {
isFirst = true;
for(var y = 0; y < data.length; y++) {
if (predicate(data[x], data[y])) {
if (!isFirst) {
data.splice(y, 1);
count++;
}
isFirst = false
}
}
}
return count;
}
var items = [[1,"07/28/2014"], [2,"07/29/2014"],[3, "07/28/2014"],[1,"07/28/2014"],[4, "07/30/2014"]];
// Now call the new function and pass in the equality test as the second parameter:
var itemsRemoved = spliceIf(items,
function(a, b) {
/*
This predicate function will be passed to spliceIf. When it
is called from within then spliceIf function, it will be
provided with the inner and outer elements of the loop.
You can then do your equality test however you see fit.
Notice the predicate function must return a value.
This is equivalent to the "data[x][1] === data[y][1]" line
in the example above.
*/
return a[1] === b[1];
}
);
console.log(itemsRemoved);
console.log(items);

Just doing some fiddles, I thought this might help. JSFiddle of Duplicate Remover Function
var data = [
[1, "07/28/2014"],
[2, "07/29/2014"],
[3, "07/28/2014"],
[4, "07/30/2014"],
[5, "07/28/2014"],
];
var data2 = [
[1, "07/28/2014"],
[2, "07/29/2014"],
[3, "07/29/2014"],
[4, "07/29/2014"],
[5, "07/29/2014"],
];
function removeDuplicates(Array){
this._newArray = [];
this.numberOfDuplicates = 0;
this.listDuplicates = [];
//Remove Duplicates
for(i=0;i<Array.length;i++){
for(j=0;j<Array.length;j++){
if(!Array[i][1]) //skip the current loop if index is empty
break;
if(Array[i][1]==Array[j][1] && i!=j){
this.listDuplicates.push(Array[j]);
Array[j]=0;
this.numberOfDuplicates+=1; //increase counter for dupes
}
}
}
//Recreate Array
this.newArray = function(){
for(i=0;i<Array.length;i++){
if(Array[i])
this._newArray.push(Array[i]);
}
return this._newArray;
}
}
var data = new removeDuplicates(data);
console.log(data.newArray());
console.log(data.numberOfDuplicates + " Duplicates");
console.log(data.listDuplicates);
console.log("\n");
var data2 = new removeDuplicates(data2);
console.log(data2.newArray());
console.log(data2.numberOfDuplicates + " Duplicates");
console.log(data2.listDuplicates);

Related

Why am I not entering this for loop

Why am I not entering this for loop?
Looks like the for loop isn't processing
let z = [1, 2];
function filter_list(l, z) {
// Why am I not entering the for loop?
let del = [];
for (let i = 0; l.length < i, i++;) {
if (z[i] === l[i]) {
console.log(l)
} else {
l[i].push(del)
console.log(l);
}
}
del = undefined
delete(del);
}
let l = [1, 2, 'a', 'b'];
filter_list(l);
missing semicolon after condition. You have a comma instead.You also have an extra semicolon after incrementation. Also you have the condition flipped like CherryDT stated
change this for(let i = 0; l.length < i, i++;){
to this for(let i = 0; i<l.length; i++){
There are a few bugs in the code that you posted - the order of arguments in the for loop, syntax, passing of variables, etc. I have attempted to explain below.
// note that this function takes two arguments "l" and "z" so they will be limited to the function scope
function filter_list(l, z) {
// this is never being used?
let del = [];
// Why am I not entering the for loop?
// check for i less than l.length
// separate using a semicolon
// prefer using i+=1
for (let i = 0; i < l.length; i+=1) {
if (z[i] === l[i]) {
// this will happen if the same element is in the same position in each array, you probably want to not specify the index?
console.log(l)
} else {
// del is an empty array here, it will just overwrite the index of l
l[i].push(del)
console.log(l);
}
}
// this is probably not needed
del = undefined
// this function is not defined and does nothing since del=undefined
delete(del);
}
// you need to pass two arguments to filter_list(), using different variable names to clarify
let one = [1, 2, 'a', 'b'];
let two = [1, 2];
filter_list(one, two);
If you want to filter one list by another a better way to do it would be to use Array.filter() and Array.includes().
const one = [1, 2, 'a', 'b'];
const two = [1, 2];
const three = one.filter((element) => two.includes(element);

Why changing the "if" condition give different answers when I use === instead of !==?

The function checks if array elements are same, if they are same it should return true.
When I use the function below, it gives the correct result.
var arr = [1, 2, 3, 4];
function isUniform(arr) {
var store = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] !== store) {
return false;
}
}
return true;
}
console.log(isUniform(arr));
But when I use the function like this i.e; changing the if condition, it returns false
var arr = [1, 2, 3, 4];
function isUniform(arr) {
var store = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === store) {
return true;
}
}
return false;
}
console.log(isUniform(arr));
The problem is that your snippet is returning true if one of the next values are the same as arr[0], see the below example:
var arr = [1, 1, 3, 1, 1];
function isUniform(arr) {
var store = arr[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === store) {
return true;
}
}
return false;
}
console.log(isUniform(arr));
Here the function returns true as soon as the for loop starts, since arr[0] is store
What you should use is Array#every() which checks if all elements match certain criteria:
var arr = [1, 1, 3, 1, 1];
var arr2 = [1,1,1,1,1,1,1];
function isUniform(arr) {
return arr.every(e => e === arr[0]);
}
console.log(isUniform(arr));
console.log(isUniform(arr2));
The firts loop is giving the rigth answer because the elements in the list are not the same, the problem with the second loops is that it checks each element in the list and when i found a element equals to the array[0] (in this case (1)) it will return true which can't be.
The second loops should be:
var arr = [1, 2, 3, 4];
function isUniform(arr) {
var store = arr[0];
var response = false;
for (var i = 0; i < arr.length; i++) {
if (arr[i] === store) {
resonse = true;
continue;
}
return response;
}
return response;
}
console.log(isUniform(arr));
Cause it will return true if the first element is the same. It won't check all elements while the second will return true if all elements are the same if one of the elements is not the same it returns false.
The probably shortest approach to this is:
(new Set(array)).size === 1
The second code snipped does not check if all elements are equal to store, it returns true if at least one is equal to store.
And the first one returns false if all of the elemens are not equal to store, and as of that true if all of them are equal to store, but to be previces it does not test if all elements equal store it's only a logical conclusion in this case.
It returns true ... You can also consider doing...
arr.includes(arr[0]) // returns true

Javascript: Write a function that takes in an array, and then returns an array with only unique numbers, only arrays removed

Write a function that takes in a list and returns a list with all of the duplicates removed (list will only have unique numbers).
Here's what I have so far:
var lista = [1,4,5,1,1,3,5,6,4,4,3];
function dupRemove (lista) {
//Sort the array in case it isn't sorted
lista.sort();
//Object to store duplicates and unique numbers
var listNumbers = {
"Duplicate Numbers": [],
"Unique Numbers": []
};
for (var i = 0; i < lista.length; i++) {
//check if it is not equal to the index of the array before it and after. if it isn't, that means its unique, push it in the uniques array.
if (lista[i] !== lista[i-1] && lista[i] !== lista[i+1]) {
listNumbers["Unique Numbers"].push(lista[i]);
} else {
listNumbers["Duplicate Numbers"].push(lista[i]);
}
}
return listNumbers;
}
Currently, my solution returns an object with keys with the values of "Duplicates": 1, 1, 1, 3, 3, 4, 4, 4, 5, 5 and "Uniques": 6.
How do I remove the duplicates from duplicates and then join these two keys into a single array?
Thank you.
that answer is seriously over -engineered- all you need to to is push all values into a new array if they are not already in it.
function=removeDups()
{
var lista = [1,4,5,1,1,3,5,6,4,4,3];
var uniqueValues=[];
var duplicateValues=[];
for(i=0;i<lista.length;i++)
{
if(uniqueValues.indexof(lista[i] == -1){uniqueValues.push(lista[i]}else{duplicateValues.push(lista[i]}
}
}
You could just use the default filter method that is on all Arrays
You don't need the sort function either. If the item is already found using the indexOf method it will not be added to the newly returned array created by the filter method
var list = [1,4,5,1,1,3,5,6,4,4,3];
function removeDup (arr) {
return arr.filter(function(item, pos) {
return arr.indexOf(item) == pos;
})
}
var sortedList = removeDup(list).sort(function(a,b){
return a - b
})
document.getElementsByTagName('div')[0].textContent = sortedList
<div></div>
Kind of a non elegant solution but it gives you the two arrays: one with the duplicate values and one with the unique ones. Since you cannot rely on .sort() you can just count things.
Function checkList will give you back those two arrays.
var list = [1,4,5,1,1,3,5,6,4,4,3];
console.log(checkList(list));
function checkList(list) {
var uniques = []; // will be [6]
var dups = []; // will be [1, 4, 5, 3]
var checked = []; // save what you have already checked so far
for(i = 0; i < list.length; i++) {
if(notChecked(list[i], checked)) {
checked.push(list[i]);
if(count(list[i], list) > 1) {
dups.push(list[i]);
} else {
uniques.push(list[i]);
}
}
}
return {dups: dups, uniques: uniques}
}
// count how many num in arr
function count(num, arr) {
var count = 0;
var i;
for(i = 0; i < arr.length; i++) {
if(arr[i] == num) count++;
if(count > 1) return count;
}
return count;
}
// check if num has not been checked
function notChecked(num, arr) {
return (arr.indexOf(num) == -1) ? true : false;
}

Iterative solution for flattening n-th nested arrays in Javascript

Can anyone show me an iterative solution for the following problem? I solved it recursively but struggled with an iterative solution. (Facebook Technical Interview Question)
Input: [1, {a: 2}, [3], [[4, 5], 6], 7]
Output: [1, {a: 2}, 3, 4, 5, 6, 7]
Solution must work with n-th nested array elements (i.e. it must still work if someone modifies the array values/placement in the example above)
Recursive solution:
var flatten = function(input) {
var result = [];
input.forEach(function(element) {
result = result.concat(Array.isArray(element) ? flatten(element) : element);
});
return result;
}
Here is one way:
var input = [1, {a: 2}, [3], [[4, 5], 6], 7];
function flatten(input) {
var i, placeHolder = [input], lastIndex = [-1], out = [];
while (placeHolder.length) {
input = placeHolder.pop();
i = lastIndex.pop() + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder.push(input);
lastIndex.push(i);
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
flatten(input);
Explanation: If iterating over a nested structure, you just have to remember where you were before by saving the current array and position before moving into the nested array (this is usually taken care of via the stack for recursive solutions).
Note: If you reuse the arrays placeHolder and lastIndex you won't need to keep recreating them every time. Perhaps something like this:
var flatten = function(){
var placeHolder = [], lastIndex = [];
placeHolder.count = 0;
lastIndex.count = 0;
return function flatten(input) {
var i, out = [];
placeHolder[0] = input; placeHolder.count = 1;
lastIndex[0] = -1; lastIndex.count = 1;
while (placeHolder.count) {
input = placeHolder[--placeHolder.count];
i = lastIndex[--lastIndex.count] + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder[placeHolder.count++] = input;
lastIndex[lastIndex.count++] = i;
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
}();
This is even faster again (for flat iteration that is), and less garbage collector issues calling it many times. The speed is very close to that of recursive function calling in Chrome, and many times faster than recursion in FireFox and IE.
I recreated Tomalak's tests here since the old jsPerf is broken for editing: https://jsperf.com/iterative-array-flatten-2
How about this?
inp = [1, {a: 2}, [3], [[4, 5], 6], 7]
out = inp;
while(out.some(Array.isArray))
out = [].concat.apply([], out);
document.write(JSON.stringify(out));
Works, but not recommended:
var flatten = function(input) {
return eval("[" + JSON.stringify(input).
replace(/\[/g,"").replace(/\]/g,"") + "]");
}
Here's a solution that flattens in place.
function flatten(arr) {
var i = 0;
if (!Array.isArray(arr)) {
/* return non-array inputs immediately to avoid errors */
return arr;
}
while (i < arr.length) {
if (Array.isArray(arr[i])) {
arr.splice(i, 1, ...arr[i]);
} else {
i++;
}
}
return arr;
}
This solution iterates through the array, flattening each element one level of nesting at a time until it cannot be flattened any more.
function flatten(array){
for(var i=0;i<array.length;i++)
if(Array.isArray(array[i]))
array.splice.apply(array,[i,1].concat(array[i--]));
return array;
}
This in-place solution is faster than Lupe's, now that I've removed all of the inner curly brackets (I inlined the i-- in the concat parameter to do that).
A different iterative algorithm:
function flatten2(input) {
var output = [];
var todo = [input];
var current;
var head;
while(todo.length) {
var current = todo.shift();
if(Array.isArray(current)) {
current = current.slice();
head = current.shift();
if(current.length) {
todo.unshift(current)
}
todo.unshift(head);
} else {
output.push(current);
}
}
return output;
}
Put all elements on a stack.
While the stack is not empty, remove the first element.
If that element is a scalar, add it to the output.
If that element is an array, split it into head (first element) and tail (remaining elements) and add both to the stack.
As Tomalak's JSPerf shows, this is pretty slow.
JSBin
A fairly concise, readable algorithm:
function flatten(input) {
var output = [];
var todo = [input];
var current;
while(todo.length) {
var current = todo.shift();
if(Array.isArray(current)) {
todo.unshift.apply(todo, current)
} else {
output.push(current);
}
}
return output;
}
This version performs better than my other answer, but is still significantly slower than James Wilkins' answer.
JSBin
Tomalak's JSPerf
Here are two approaches, recursive and iterative and their comparison to Array.flat.
Maybe it'll help someone
const arrayToFlatten = [[1], [2, [3]], null, [[{}]], undefined];
// takes an array and flattens it recursively, default depth is 1 (just like Array.flat())
function flattenRecursive(arr, depth = 1) {
let myArray = [];
if (depth === 0){ // if you've reached the depth don't continue
myArray = arr;
} else if(!Array.isArray(arr)) { // add item to array if not an array
myArray.push(arr);
} else { // flatten each item in the array then concatenate
arr.forEach(item => {
const someNewArray = flattenRecursive(item, depth - 1);
myArray = myArray.concat(someNewArray);
});
}
return myArray;
}
// takes an array and flattens it using a loop, default depth is 1 (just like Array.flat())
function flattenIterative(arr, depth = 1) {
let result = arr;
// if an element is an array
while(result.some(Array.isArray) && depth) {
// flatten the array by one level by concating an empty array and result using apply
result = [].concat.apply([], result);
depth--; // track depth
}
return result;
}
console.log(arrayToFlatten.flat(2)); // ES^
console.log(flattenRecursive(arrayToFlatten, 2));
console.log(flattenIterative(arrayToFlatten, 2));
Here's my solution to this:
function flattenList(A) {
let result = []
for (let i=0; i < A.length; i++) {
if (typeof A[i] == "object"){
let item = reduceArray(A[i])
result.push(...item)
}else {
result.push(A[i])
}
}
return result
}
function reduceArray(arr){
while(arr.some(Array.isArray)) {
let item = arr.find(Array.isArray)
let index = arr.indexOf(item)
arr[index] = item[0]
}
return arr
}
Not sure if the "stack" approach was used properly in previous answers. I think it could be simpler, like this:
function flatten(arr) {
const result = [];
const stack = [arr];
while (stack.length) {
const curr = stack.pop();
if (Array.isArray(curr)) {
for (let i = curr.length - 1; i >= 0; i--) {
stack.push(curr[i]);
}
} else {
result.push(curr);
}
}
return result;
}
Not sure why the other answers are so complicated, this can easily be achieved by looping through the array and flattening each entry until it's no longer an array.
const flatten = (arr) => {
for (let i = 0; i < arr.length; i++) {
while (Array.isArray(arr[i])) {
arr.splice(i, 1, ...arr[i]);
}
}
return arr;
}

Javascript matrix addition of an arbitrary number of arrays' values, without using array.map()

I'm trying to build a helper function for my AngularJS app (but the solution doesn't have to use angular.forEach). The function should take an arbitrary number of arrays with same length and add the values together to form an new array, e.g.:
a = [1, 2, 3];
b = [4, 5, 6];
result = [5, 7, 9];
I'm trying to not use array.map() or sylvesterjs for IE8 compatibility. I'm stuck on looping through arguments as javascript doesn't seem to like nesting functions within a for loop. Code so far:
function arrayAdd () {
// make sure all args have same length
var totalLen = 0;
angular.forEach(arguments, function (arg) {
totalLen += arg.length;
});
var avgLen = totalLen / arguments.length;
if (avgLen === arguments[0].length) {
var arrNew = mkArray(0, arguments[0].length);
// helper function to make an empty array, not shown here
// need to refactor below with loop for unlimited # of args
angular.forEach(arguments, function (arg, i) {
arrNew[0] += arg[0];
});
angular.forEach(arguments, function (arg, i) {
arrNew[1] += arg[1];
});
return arrNew;
} else {
throw 'arrayAdd expects args with equal length';
}
}
Thanks!
No need for map, you only need to keep track of the arrays as you total them.
(This does not check that the arrays have the same length-)
function sumArrayElements(){
var arrays= arguments, results= [],
count= arrays[0].length, L= arrays.length,
sum, next= 0, i;
while(next<count){
sum= 0, i= 0;
while(i<L){
sum+= Number(arrays[i++][next]);
}
results[next++]= sum;
}
return results;
}
var a= [1, 2, 3], b= [4, 5, 6], c= [1, 2, 3];
sumArrayElements(a, b, c)
/* returned value:(Array)
6, 9, 12
*/
Why don't you just add it to the array prototype if it doesn't exist?
if (!('map' in Array.prototype)) {
Array.prototype.map= function(mapper, that /*opt*/) {
var other= new Array(this.length);
for (var i= 0, n= this.length; i<n; i++)
if (i in this)
other[i]= mapper.call(that, this[i], i, this);
return other;
};
}
Figured it out, didn't need array.map(). Writing the arrays and loops out on paper helped. Hope this is helpful for someone else.
function arrayAdd () {
// ensure all args have same length
var totalLen = 0;
angular.forEach(arguments, function (arg) {
totalLen += arg.length;
});
var difLen = arguments[0].length === totalLen / arguments.length;
if (difLen) {
var arrNew = mkYrArray(0, arguments[0].length, 0);
// helper function to make an empty array, not shown here
angular.forEach(arguments, function (arr, i1) {
// each loop is an array from arguments
angular.forEach(arr, function (arrValue, i2) {
// each loop is a value from array
arrNew[i2] += arrValue;
});
});
return arrNew;
} else {
throw 'arrayAdd expects args w/ equal len';
}
}

Categories

Resources