find duplicate values in array of objects in javascript - javascript

I have two lists in javascript that are of same structure like below:
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
I need to remove all records from database that are in existing documents list (i.e "dt") but NOT in required_documents list.
For the above scenario I should remove only {"id":3,"dt":4} and insert {"id":3,"dt":3}. I am not sure how I can compare on just one property. This is below that I found on SOF sometime ago but can't find it again apologies for not referencing it.
required_documents.forEach((obj) => {
const elementInArr2 = existing_documents.find((o) => o.dt === obj.dt);
console.log('found elementinarr: ' + obj.dt);
});
This returns unique objects like dt:1,dt:2,dt:3 but I need dt:4 from the existing documents list as it is the one that is not in the required documents list and needs to be deleted. How can I get just the one that is not in the required documents list.

Assuming both id and dt properties are significant, I would first create a means of hashing an entry and then build a hashed set of required_documents.
Then you can filter out anything from existing_documents that is in the set, leaving only the results you want.
const required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
const existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}];
// a simple stringify hash
const createHash = ({ id, dt }) => JSON.stringify({ id, dt });
const requiredHashSet = new Set(required_documents.map(createHash));
const result = existing_documents.filter(
(doc) => !requiredHashSet.has(createHash(doc))
);
console.log(result);
The hash creation can be anything that produces a comparable entity that can uniquely identify a record.

You need to run it twice to confirm there is no elements left in existing. So create a function and use it.
var required_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":3}];
var existing_documents = [{"id":1,"dt":1},{"id":2,"dt":2},{"id":3,"dt":4}]
let output = [];
output = output.concat(extractUniqueValues(required_documents, output));
output = output.concat(extractUniqueValues(existing_documents, output));
console.log(output)
function extractUniqueValues(input, output){
return input.filter((item)=>{
return !output.find(v => v.dt == item.dt)
})
}

You can do like below
var required_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 3 },
];
var existing_documents = [
{ id: 1, dt: 1 },
{ id: 2, dt: 2 },
{ id: 3, dt: 4 },
];
for (let index = 0; index < required_documents.length; index++) {
const element = required_documents[index];
for (var i = existing_documents.length - 1; i >= 0; i--) {
const child = existing_documents[i];
if (element.id === child.id && element.dt === child.dt) {
existing_documents.splice(i, 1);
} else {
required_documents.push(element);
}
}
}
LOG not exist [{"dt": 4, "id": 3}]
LOG unique items [{"dt": 1, "id": 1}, {"dt": 2, "id": 2}, {"dt": 3, "id": 3}]

If you don't care about time complexity, something this should work:
var new_documents = existing_documents.filter(ed => {
return required_documents.find(rd => rd.dt == ed.dt);
});
Edit Okay, I just reread your question and I'm a bit confused. Do you want the object {id: 3, dt: 3} inside the new array as well?

Related

JS - Filter array of objects returning only a specific field (some kind of combination between map and filter)

I have the following array
const arr = [
{ id: 1, token: "aAdsDDwEwe43svdwe2Xua" },
{ id: 2, token: undefined }
];
And I need to filter out undefined tokens, and ignore the id field.
Something like:
const arr = [
{ id: 1, token: "aAdsDDwEwe43svdwe2Xua" },
{ id: 2, token: undefined },
];
const result = arr
.filter(({ token }) => token !== undefined)
.map(({ token }) => token);
console.log(result);
Is it possible to do it in O(n) ? I mean, without navigating through the list twice.
const result = arr.reduce((acc,curr) => {
return curr.token !==undefined ? [...acc,curr.token] : acc
},[])
Firstly it is O(n). Just because we run over a loop two times, it does not become O(n^2).
Additionally, if you simply use a for loop you will realise how simple it is:
const arr = [
{ id: 1, token: "aAdsDDwEwe43svdwe2Xua", extraField : "x" },
{ id: 2, token: undefined, extraField : "x" }
];
let ans = [];
for(let i = 0 ; i < arr.length; i++){
if(arr[i].token!== undefined){
let { id, ...newBody } = arr[i];
ans.push(newBody);
}
}
console.log(ans);
Used spread operator (...), to remove a particular property (here id).
If you are looking for array methods, the above could also be achieved using a .forEach()

how to check for properties in an array

I am making a service call where I get back some data. For example:
var response = [
{id: 1, name: 'text1'},
{id: 2, name: 'text2'}
];
This array represents the most possible data I could get back. In other instances I may get back an empty array, an array with only one of those objects(either or of the objects inside the array).
I'd like to somehow loop through my array and check the id of each on=bject to set a flag for the front end to display other data. How do I properly do checks for the id's in the array for the scenarios I mentioned above?
Use Array.some()
var response = [{ id: 1, name: "text1" }, { id: 2, name: "text2" }];
var exists = (id, arr) => arr.some(e => e.id === id);
var id1Exists = exists(1, response);
var id2Exists = exists(2, response);
var id3Exists = exists(3, response);
console.log({ id1Exists, id2Exists, id3Exists });
var response = [
{id: 1, name: 'text1'},
{id: 2, name: 'text2'}
];
let flag
response.length > 0 && response.forEach(el => {
// JUST CHECKING IF ID IS PRESENT AND EQUAL TO 1
// YOU CAN CHANGE ACCORDINGLY
flag = el.id && el.id === 1 ? true : false
})
console.log(flag)
Here I write a function to find a certain object inside your array, I think this must help you:
public function getMyId($myArray, $id)
{
foreach($myArray as $key => $element){
$arrayFromObject = (Array) $element;
foreach($arrayFromObject as $keyObject => $valueObject){
if($valueObject['id'] == $id)
{
return $myArray[$key]->$keyObject;
}
}
}
return null;
}
For each element inside your array I convert object to array to verify if its id is equal to id u're looking for, than I return the array at certain position and certain value, or if you prefer u could just return the $valueObject it will be an array. I hope help u;

Compare array objects and show difference

I have two arrays which I want to compare and check if there is an deleted item in one of these arrays. If there is show me the difference (deleted item)
Here is the code below how I would like to achieve this:
var completedList = [{id:1},{id:2},{id:3},{id:4},{id:7},{id:8}];
var invalidList = [{id:3},{id:4},{id:5},{id:6}];
// filter the items from the invalid list, out of the complete list
var validList = completedList.map((item) => {
console.log(item.id)
return item.id;
//console.log(invalidList.id);
}).filter(item => {
Object.keys(invalidList).map(key => {
console.log(invalidList[key].id)
//return !invalidList[key].id.includes(item.id);
});
})
console.log(validList); // Print [1,2,7,8]
// get a Set of the distinct, valid items
var validItems = new Set(validList);
But this returns me a lot of id's how can I map through both array's and filter on object property id? And only show the difference between these array objects.
So basically what I expect is to see the difference between those arrays so log the differences in id's so in this example: 1,2,5,6,7,8
You could take a Set for getting a difference. For getting the differences from each other (a symmetric difference), you need to get both differences.
const
difference = (a, b) => Array.from(b.reduce((s, v) => (s.delete(v), s), new Set(a))),
getId = ({ id }) => id;
var completedList = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 7 }, { id: 8 }],
invalidList = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }],
complete = completedList.map(getId),
invalid = invalidList.map(getId),
left = difference(complete, invalid),
right = difference(invalid, complete),
result = [...left, ...right]
console.log(result.join(' '));
console.log(left.join(' '));
console.log(right.join(' '));
This should do the trick.
let completedList = [{id:1},{id:2},{id:3},{id:4},{id:7},{id:8}];
let invalidList = [{id:3},{id:4},{id:5},{id:6}];
// filter the items from the invalid list, out of the complete list
let temp1 = completedList.map(e => e.id);
let temp2 = invalidList.map(e => e.id);
let validList = temp1.filter(e => temp2.indexOf(e) === -1);
// find items only in invalidList
let difference = temp2.filter(e => temp1.indexOf(e) === -1);
console.log(validList); // Print [1,2,7,8]
console.log(difference);
I often rely on lodash implementation for comparison.
In lo dash you can get the job done following manner
_.intersectionWith(arr1, arr2, _.isEqual) - For similarity
_.differenceWith(arr1, arr2, _.isEqual) - for differences
This ans is confined to using a util library to get the job done.
If you are looking for the exact algo I would definitely take some time to develop it and reply as a comment to this post .
Thanks
var completedList = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 7 }, { id: 8 }];
var invalidList = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }];
//get the items that are in the invalid list but not completed list
var filteredList1 = invalidList.filter((invalidListItem) => !completedList.find((item) => item.id === invalidListItem.id));
//get the items that are in the completed list but not in the invalid list
var filteredList2 = completedList.filter((completedListItem) => !invalidList.find((item) => item.id === completedListItem.id));
//join the two arrays
var difference = filteredList1.concat(filteredList2);
//display the merged array and sort
console.log(difference.sort((item1, item2) => { return item1.id > item2.id ? 1 : item1.id < item2.id ? -1 : 0; }));
//outputs 1,2,5,6,7,8

Add unique generated Identifier on array of json object

How to add uniqueId field in below JSON. This array has large number of data and needs to dynamic unique identifier on existing array.
[{"title":"Accompanying"},{"title":"Chamber music"},{"title":"Church
music"}......]
so, this should look as follow:
[{"title":"Accompanying", "uniqueId": 1},{"title":"Chamber music", "uniqueId": 2}..]
uniqueId- type, number or guid.
Note: don't know the "title" or what other fields could be, so, could not map the fields by name.
I would go for a simple for loop
let myArray = [{"title":"Accompanying"},{"title":"Chamber music"},{"title":"Church music"}];
let i = 0, ln = myArray.length;
for (i;i<ln;i++){
myArray[i].uniqueId = i+1;
}
console.log(myArray);
If this is a one time thing you could do the following:
const newArray = oldArray.map((x, i) => ({
// If the object is dynamic you can spread it out here and add the ID
...x,
// Use the items index in the array as a unique key
uniqueId: i,
}));
If you want to use a guid generator instead (I'd recommend that) just replace i with whatever you use to generate a GUID and ensure that any time you add to the collection you generate a new GUID for the data.
const newArray = oldArray.map((x) => ({ ...x, uniqueId: generateGuid() }));
const yourDynamicObjects = [
{
title: 'A title',
author: 'A. Author'
},
{
foo: 'bar',
},
{
quotient: 2,
irrational: Math.sqrt(2)
}
];
const updatedData = yourDynamicObjects.map((x, i) => ({ ...x, uniqueId: i, }));
console.log(updatedData);
You can use map & in it's call back function use the index parameter to create uniqueId
item.title is not known actually as its dynamic array and so, could
not map with particular field names
In this case use Object.keys to get an array of all the keys . Then loop over it and add the key to a new object
let k = [{
"title": "Accompanying"
}, {
"title": "Chamber music"
}, {
"title": "Church"
}]
let getArrayKey = Object.keys(k[0]);
let n = k.map(function(item, index) {
let obj = {};
getArrayKey.forEach(function(elem) {
obj[elem] = item[elem];
})
obj.uniqueId = index + 1
return obj;
});
console.log(n)
Also you can use spread operator
let k = [{
"title": "Accompanying"
}, {
"title": "Chamber music"
}, {
"title": "Church"
}]
let n = k.map(function(item, index) {
return Object.assign({}, { ...item,
uniqueId: index + 1
})
});
console.log(n)

JS RegEx: strings to JSON object

I need to convert this:
-[firstLink](https://webapp.com/topic/ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6)
--[secondLink](https://webapp.com/source/d93f154c-fb1f-4967-a70d-7d120cacfb05)
-[thirdLink](https://webapp.com/topic/31b85921-c4af-48e5-81ae-7ce45f55df81)
into this:
const obj = [{
name: 'firstLink',
type: 'topic',
id: 'ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6',
spacing: 1, // single "-"
}, {
name: 'secondLink',
type: 'source',
id: 'd93f154c-fb1f-4967-a70d-7d120cacfb05',
spacing: 2, // two "-"
}, {
name: 'thirdLink',
type: 'topic',
id: '31b85921-c4af-48e5-81ae-7ce45f55df81',
spacing: 1,
}]
Here what i'm trying now:
const items = text.split('\n');
const re = /(-+)\[([^\]]+)\]\(([^)"]+)\)/
const arrays = items.map(function (item) {
return item.split(re);
})
for some reason, arrays contains bunch of empty strings ("") at the start and end of each item.
1. So, first question, is how to get rid of it?
2. Is there alternative to this regex tester, which actually shows such issues? This one doesn't show empty strings in results and, probably, some other issues.
Next, i'll do this:
const result = arrays.map(function (singleArray) {
if (!singleArray[1]) return null
const spacing = singleArray[1].length;
const name = singleArray[2]
const typeRe = /(source|topic)/;
const uuidRe = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/;
const type = typeRe.exec(singleArray[3])[1]
const id = uuidRe.exec(singleArray[3])[1]
return {
name, type, id, spacing,
}
})
And third question: this code looks very ugly, what am i doing wrong?
I think you're trying to solve this problem with a lot of regexp when the first one you have is enough to solve this problem
So for the matches got for the regex matches = line.match(re)
matches[1] represents the number of hyphens, therefore separator = nHyphens.length
matches[2] is the hyperlink alt and for your case the name
matches[3] is the hyperlink src which needs to be split by /, the two last items are type and id, note that this assumes that the src will always have the same structure
function solve(content) {
const re = /(-+)\[([^\]]+)\]\(([^)"]+)\)/
return content.split('\n').map(line => {
// name,type,id,spacing
let [full, hyp, alt, src] = line.match(re)
src = src.split('/')
return {
name: alt,
spacing: hyp.length,
id: src[src.length - 1],
type: src[src.length - 2]
}
})
}
const lines = `-[firstLink](https://webapp.com/topic/ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6)
--[secondLink](https://webapp.com/source/d93f154c-fb1f-4967-a70d-7d120cacfb05)
-[thirdLink](https://webapp.com/topic/31b85921-c4af-48e5-81ae-7ce45f55df81)`
console.log(solve(lines))
And this would be my solution with str.match() without using any capture groups, RegExp.exec() method and while loops.
var reg = /-+(?=\[)|[^[]+(?=])|[a-z]+(?=\/\w*-)|[\w-]+(?=\))/g,
data = '-[firstLink](https://webapp.com/topic/ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6)\n--[secondLink](https://webapp.com/source/d93f154c-fb1f-4967-a70d-7d120cacfb05)\n-[thirdLink](https://webapp.com/topic/31b85921-c4af-48e5-81ae-7ce45f55df81)',
obj = data.match(reg).reduce((p,c,i) => (i%4 ? p[p.length-1].push(c) : p.push([c]) ,p),[])
.map(e => ({"name": e[1], "type": e[2], "id": e[3], "spacing": e[0].length}));
console.log(obj);
The needed array of objects can be obtained with few lines of code using String.split, String.match and Array.map functions(and I suppose, the following code doesn't look ugly at all):
const items = text.split(/\n/).map(function(v){
var parts = v.match(/([-]+?)\[(\w+?)\]\(https?:\/\/[^/]+\/(\w+?)\/([^)]+)\b/);
return {'name': parts[2], 'type': parts[3], 'id': parts[4], 'spacing': parts[1].length};
});
console.log(JSON.stringify(items, 0, 4));
The output:
[
{
"name": "firstLink",
"type": "topic",
"id": "ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6",
"spacing": 1
},
{
"name": "secondLink",
"type": "source",
"id": "d93f154c-fb1f-4967-a70d-7d120cacfb05",
"spacing": 2
},
{
"name": "thirdLink",
"type": "topic",
"id": "31b85921-c4af-48e5-81ae-7ce45f55df81",
"spacing": 1
}
]
Here you go:
const array = text.split(/\n/);
let box, sliced;
const obj = array.map(line => {
box = {};
sliced = line.match(/\[[a-z]+\]/i)[0];
box.name = sliced.slice(1, sliced.length - 1);
box.spacing = line.match(/\-+/)[0].length;
box.type = line.replace(/.*\.com\/([a-z]+)\/.*/, '$1');
box.id = line.replace(/.*\/([a-z0-9\-]+)\)$/, '$1')
return box;
});
I think it's better to use RegEx.exec. Please try as my script.
const regex = /(-+)\[(\w+)\]\(([^)]+)\)/img;
const data = '-[firstLink](https://webapp.com/topic/ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6)\n--[secondLink](https://webapp.com/source/d93f154c-fb1f-4967-a70d-7d120cacfb05)\n-[thirdLink](https://webapp.com/topic/31b85921-c4af-48e5-81ae-7ce45f55df81)';
var groups, items = [];
while(groups = regex.exec(data)){
items.push({
spacing: groups[1],
name: groups[2],
link: groups[3]
})
}
If your links are always in the same domain, extracting type and id could be place in single regex.
const regex = /(-+)\[(\w+)\]\(https:\/\/webapp\.com\/(\w+)\/([^)]+)\)/img;
const data = '-[firstLink](https://webapp.com/topic/ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6)\n--[secondLink](https://webapp.com/source/d93f154c-fb1f-4967-a70d-7d120cacfb05)\n-[thirdLink](https://webapp.com/topic/31b85921-c4af-48e5-81ae-7ce45f55df81)';
var groups, items = [];
while(groups = regex.exec(data)){
items.push({
spacing: groups[1].length,
name: groups[2],
type: groups[3],
id: groups[4]
})
}
Regarding your question on why your split result contains two additional empty strings, you should read section Capturing parentheses in this guide
Hope this will help.

Categories

Resources