how to remove redundant array objects - javascript

I need to remove all the duplicate elements, for example:
var arr = [
{'seriesIndex':1,pointIndex:0},
{'seriesIndex':1,pointIndex:1},
{'seriesIndex':0,pointIndex:0},
{'seriesIndex':1,pointIndex:0},
{'seriesIndex':1}
]
How to remove redundant (duplicate) array objects from an array?
My expected output is:
arr = [
{'seriesIndex':1,pointIndex:0},
{'seriesIndex':1,pointIndex:1},
{'seriesIndex':0,pointIndex:0},
{'seriesIndex':1}
]
How to achieve this?

In vanilla Javascript, I'd suggest to keep track of the encountered composite keys while iterating on the list in a .reduce() method. The test on prv.key[key] is O(1) (hashtable lookup), so this algorithm is O(n).
var arr = [
{seriesIndex:1, pointIndex:0},
{seriesIndex:1, pointIndex:1},
{seriesIndex:0, pointIndex:0},
{seriesIndex:1, pointIndex:0},
{seriesIndex:1}
];
arr = arr.reduce(function(prv, cur) {
var key = cur.seriesIndex + '/' + cur.pointIndex;
if(!prv.key[key]) {
prv.key[key] = true;
prv.res.push(cur);
}
return prv;
}, {key: {}, res: []}).res;
console.log(arr);

I use library, which has rich API - lodash
With lodash it looks like :
_.uniqWith(arr, _.isEqual)
It is simple and short
Link to library https://lodash.com/

By an invention of Object.prototype.compare() you may do like this
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
var arr = [{'seriesIndex':1,pointIndex:0},
{'seriesIndex':1,pointIndex:1},
{'seriesIndex':0,pointIndex:0},
{'seriesIndex':1,pointIndex:0},
{'seriesIndex':1}
],
result = arr.reduce((res,obj) => res.length === 0 ? res.concat(obj)
: !!res.find(o => obj.compare(o)) ? res
: res.concat(obj),[]);
console.log(JSON.stringify(result,null,2));

Related

How can I compare two 2D arrays? In javascript

I have a problem to compare two 2D arrays in javascript.
It should output "true" if the two 2D arrays are the same.
The code I tried:
`
function check(){
if (data.every() === solution.every()){
alert("example");
} else {
console.log("Data Array: " + data);
console.log("Solution Array: " + solution);
}
}
`
I think my solution only works with two 1D arrays. I hope someone can help me and teach me something.
Note: I only use jquery and nativ js.
Thanks in advance.
~Luca
I would assume something like this would work :
function test(array1, array2)
{
if(array1.length !== array2.length) return false;
for(let i=0;i<array1.length;i++)
{
if(array1[i].length !== array2[i].length) return false;
for(let j=0;j<array1[i].length;j++)
{
if(array1[i][j] !== array2[i][j]) return false;
}
}
return true;
}
You could also do a recursive version that would work for array of N-dimensions, but it you need only 2, the above is fine.
This code will work for any number of dimensions:
const random2dArray = (n,m) => Array(n).fill(0)
.map(_=>Array(m).fill(0).map(_=>10*Math.random()|0))
const arrEq = (a,b) => Array.isArray(a) ?
a.length===b.length && a.every((e,i)=>arrEq(e,b[i])) : a===b
const a = random2dArray(3,3)
const b = random2dArray(3,3)
const c = random2dArray(3,5)
const d = random2dArray(6,7)
const e = structuredClone(a)
console.log(arrEq(a,b))
console.log(arrEq(a,c))
console.log(arrEq(a,d))
console.log(arrEq(a,e))
You can simply achieve this requirement by following below steps :
flatten both the 2D arrays into a 1D array.
Check the length of both the arrays along with the each item check using Array.every() method.
Live Demo :
const data = [[1,2,3], [4,5]];
const solution = [[1,2,3], [4,5]];
const res = (data.flat().length === solution.flat().length) && data.flat().every((item, index) => item === solution.flat()[index]);
console.log(res ? 'Both arrays are equal' : 'not equal');
A common solution that many people suggest is to use JSON.stringify(). This allows us to serialize each array and then compare the two serialized strings. A simple implementation of this might look something like this:
function check(){
if ( JSON.stringify(data) === JSON.stringify(solution) ){
alert("example");
} else {
console.log("Data Array: " + data);
console.log("Solution Array: " + solution);
}
}

Filter array for objects with nested array that contains a specific string

I am trying to filter a large multi dimensional array, I know I can re build the array but I was wondering if there was a way of me filtering this array based on if the key "d" contains "d3.1"
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
I have tried using .some and .filter but as it's an object within an array of objects I can't seem to get it to work.
My required output would be to have all objects where d contains "d3.1" for example:
output:
[
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
Here is a quick solution:
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
console.log(arr.filter(el => JSON.stringify(el).includes("d3.1")))
If you want to filter the array for elements with property c containing at least one element (i.e. .some(...)) with property d being equal to "d3.1" you can use this solution:
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
const filtered = arr.filter(e => e.c.some(x => x.d == "d3.1"))
console.log(filtered)
Otherwise, please specify your requirements closer...
You could use a recursive approach to find matching array elements, creating a function like findMatch() that accepts a customizable predicate function.
This function could return true for exact matches or use regex expressions etc.
We'd then using this with Array.filter() to return the required result.
const arr = [ {a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]}, {a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]}, {a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}, {a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]}, {a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]}, {a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]} ]
function findMatch(obj, predicate) {
for(let k in obj) {
if (obj[k] && typeof(obj[k]) === 'object') {
if (findMatch(obj[k], predicate)) return true;
} else if (predicate(k, obj[k])) {
return true;
}
}
}
// Custom predicate, change as needed.
const predicate = (key, value) => key === 'd' && value === 'd3.1';
const result = arr.filter(el => findMatch(el, predicate));
console.log('Result:');
result.forEach(el => console.log(JSON.stringify(el)));
.as-console-wrapper { max-height: 100% !important; }
You can use nested foreach to get your desired output, I have consoled both array(one containing the output you want and the other containing the indexes of those outputs)
values_ar = [];
index_ar = [];
arr.forEach((items, indexs)=>{
const test = arr[indexs].c;
test.forEach((item, index)=>{
if(test[index].d == "d3.1"){
values_ar.push(items)
index_ar.push(indexs)
}
})
})
console.log(values_ar)
console.log(index_ar)
Hope that answers your question.

clone js array w/ objects and primitives

Given the array:
['1', {type:'2'}, ['3', {number: '4'}], '5']
I need to make a clone without using the slice, json.parse and other methods.
At the moment, the code is working, but it will not clone objects:
var myArr =['1',{type:'2'},['3',{number:'4'}],'5'];
var arrClone=[];
for(var i=0;i<myArr.length;i++){
if(typeof(myArr[i])==='object')
{
var arr=[];
for(var j=0;j<myArr[i].length;j++){
arr[j]=myArr[i][j];
}
arrClone[i]=arr;
}else { arrClone[i]=myArr[i]}
}
You could check if an object is supplied and if so, another check is made for an array. Then retrn either the cloned array or the object, or the value itself.
function getClone(value) {
if (value && typeof value === 'object') {
return Array.isArray(value)
? value.map(getClone)
: Object.assign(
...Object.entries(value).map(([k, v]) => ({ [k]: getClone(v) }))
);
}
return value;
}
var data = ['1', { type: '2' }, ['3', { number: '4' }], '5'],
clone = getClone(data);
console.log(getClone(data));
Here is a simple implementation without Array methods:
function deepClone(obj) {
if (typeof obj !== "object" || obj === null) return obj; // primitives
// It could be an array or plain object
const result = obj.constructor.name == "Array" ? [] : {};
for (const key in obj) {
result[key] = deepClone(obj[key]); // recursive call
}
return result;
}
// Demo
var myArr =['1',{type:'2'},['3',{number:'4'}],'5'];
var arrClone = deepClone(myArr);
console.log(arrClone);
Note that this only works for simple data types. As soon as you start to work with Dates, regex objects, Sets, Maps, ...etc, you need much more logic. Also think of self references and functions, and how they should be dealt with.
For more advanced cloning see How to Deep Clone in JavaScript, but expect the use of several methods.

How can I map an array with duplicate values to a unique array in Javascript?

I have the following array:
var tst =
[
{"topicId":1,"subTopicId":1,"topicName":"a","subTopicName":"w"},
{"topicId":1,"subTopicId":2,"topicName":"b","subTopicName":"x"},
{"topicId":1,"subTopicId":3,"topicName":"c","subTopicName":"y"},
{"topicId":2,"subTopicId":4,"topicName":"c","subTopicName":"z"}
]
Is there an easy way that I can map that to this kind of array where topicId > id and topicName > name:
var t =
[
{"id":1,"name":"a"},
{"id":2,"name":"c"}
]
I am using a modern browser and I also have _lodash if that helps. Note that there will be about 100 rows in the tst array so I don't need a very optimized solution. A simple and easy to maintain solution would be more important.
MOST RECENT
_.uniqBy is now preferable
Full working example here
var tst = [
{"topicId":1,"subTopicId":1,"topicName":"a","subTopicName1":"w"},
{"topicId":2,"subTopicId":2,"topicName":"b","subTopicName2":"x"},
{"topicId":3,"subTopicId":3,"topicName":"c","subTopicName3":"y"},
{"topicId":1,"subTopicId":4,"topicName":"c","subTopicName4":"z"}
];
var result = _.map(_.uniqBy(tst, 'topicId'), function (item) {
return {
id: item.topicId,
name: item.topicName
};
});
console.log(result);
LEGACY
http://lodash.com/docs#uniq is a good start
_.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
Your code would look like this to get topics unique by id
var t = _.uniq(tst, 'topicId');
EDIT
I made a jsfiddle
http://jsfiddle.net/q5HNw/
UPDATE
Removed unnecessary uniqueness of names
http://jsfiddle.net/q5HNw/1/
I am one of those guys that use native functions :)
var results = tst.reduce(function(res,topic){
var exists = res.some(function(t){ return (t.id === topic.topicId && t.name === topic.topicName);});
if (!exists){
res.push({"id": topic.topicId, "name": topic.topicName});
}
return res; },[]);
Lodash version
I am not an expert using lodash, probably I will try something like this:
var results = _.reduce(tst, function(res, topic){
var exists = _.findIndex(res, function(t){
return (t.id === topic.topicId && t.name === topic.topicName);
});
if (exists === -1){
res.push({"id": topic.topicId, "name": topic.topicName});
}
return res;
},[]);
Using ECMAScript 2015 Array.prototype.find()
The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
let tst = [
{"topicId":1,"subTopicId":1,"topicName":"a","subTopicName":"w"},
{"topicId":1,"subTopicId":2,"topicName":"b","subTopicName":"x"},
{"topicId":1,"subTopicId":3,"topicName":"c","subTopicName":"y"},
{"topicId":2,"subTopicId":4,"topicName":"c","subTopicName":"z"},
];
let t = [];
tst.forEach(obj => {
// Check if the id already exists in the array 't'
if (!t.find((self) => self.id === obj.topicId)) {
// If not, pushes obj to t
t.push({
id: obj.topicId,
name: obj.topicName
});
}
});
console.log(t);
You can also compare more than one property:
let tst = [
{"topicId":1,"subTopicId":1,"topicName":"a","subTopicName":"w"},
{"topicId":1,"subTopicId":2,"topicName":"b","subTopicName":"x"},
{"topicId":1,"subTopicId":3,"topicName":"c","subTopicName":"y"},
{"topicId":2,"subTopicId":4,"topicName":"c","subTopicName":"z"},
];
let t = [];
tst.forEach(obj => {
// Check if the 'id' and 'subId' already exist in t
if (!t.find((self) => self.id === obj.topicId && self.subId === obj.subTopicId)) {
// If not, pushes obj to t
t.push({
id: obj.topicId,
subId: obj.subTopicId,
name: obj.topicName
});
}
});
console.log(t);

Doing a "Diff" on an Associative Array in javascript / jQuery?

If I have two associative arrays, what would be the most efficient way of doing a diff against their values?
For example, given:
array1 = {
foreground: 'red',
shape: 'circle',
background: 'yellow'
};
array2 = {
foreground: 'red',
shape: 'square',
angle: '90',
background: 'yellow'
};
How would I check one against the other, such that the items missing or additional are the resulting array. In this case, if I wanted to compare array1 within array2, it would return:
array3 = {shape: 'circle'}
Whilst if I compared array2 within array1, it would return:
array3 = {shape: 'square', angle: '90'}
Thanks in advance for your help!
Try this:
function diff(obj1, obj2) {
var result = {};
$.each(obj1, function (key, value) {
if (!obj2.hasOwnProperty(key) || obj2[key] !== obj1[key]) {
result[key] = value;
}
});
return result;
}
If you're familiar with PHP syntax, take a look at http://phpjs.org/functions/index which includes almost all PHP's array related functions converted into JavaScript – including array_diff
RaYell's solution is nice but unfortunately will only tell you the items in obj2 that are either different from or non-existant in obj1, if we need to know both sides, let's get all keys and then compare. The following function will return an associative array with key values for each object. Oh... to be fair, I haven't tested yet, but this should work.
var diff = function(obj1,obj2) {
var newObj = $.extend({},obj1,obj2);
var result = {};
$.each(newObj, function (key, value) {
if (!obj2.hasOwnProperty(key) || !obj1.hasOwnProperty(key) || obj2[key] !== obj1[key]) {
result[key] = [obj1[key],obj2[key]];
}
});
return result;
}
Oh, and while I do recognize that the first solution answered the initial question, I think the above solution offers another approach that the initial user might find useful so as to not require checking twice.
This might be much more sophisticate than what you need, but you can try my jsondiffpatch lib that will diff any pair of javascript objects:
https://github.com/benjamine/jsondiffpatch
if you want to test it you can see it live in http://benjamine.github.com/jsondiffpatch/demo/index.html
A minha ficou assim:
function diff(obj1, obj2){
var result = {};
for(var key1 in obj1){
let resposta = {
before : obj1[key1] ? obj1[key1] : '',
after : obj2[key1] ? obj2[key1] : ''
};
if(resposta.before !== resposta.after){
result[key1] = resposta;
}
}
for(var key2 in obj2){
if(!(key2 in result) || (key2 in obj1)){
let resposta = {
before : obj1[key2] ? obj1[key2] : '',
after : obj2[key2] ? obj2[key2] : ''
}
if(resposta.before !== resposta.after){
result[key2] = resposta;
}
}
}
return (Object.assign({}, result));
}

Categories