Remove JSON object with same id recursively - javascript

I have a JSON list with duplicates I need to remove, but I can't find a way to do it.
This is the solution that I have.
I want to keep the first item found with a given ID, and remove the next ones with the same ID.
The problem is, it tries to remove even the first item.
var gindex = [];
function removeDuplicate(list) {
$.each(list, function(i, val){
console.log(val.id);
console.log(gindex);
if($.inArray(val.id, gindex) == -1) { //in array, so leave this item
gindex.push(val.id);
}
else // found already one with the id, delete it
{
list.splice(i, 1);
}
if(val.children) {
val.children = removeDuplicate(val.children);
}
});
return list;
}
gindex = [];
list = removeDuplicate(parsed_list);
console.log(window.JSON.stringify(list));
finally, this is the original list :
[
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2, // with my algorithm, this one get also flagged for deletion
}
]
},
{
"id": 2, // remove this one
},
{
"id": 3,
},
{
"id": 4, // with my algorithm, this one get also flagged for deletion
"children": [
{
"id": 5, // with my algorithm, this one get also flagged for deletion
"children": [
{
"id": 6, // with my algorithm, this one get also flagged for deletion
}
]
}
]
},
{
"id": 5, // remove this one
"children": [
{
"id": 6, // remove this one
}
]
},
{
"id": 6, // remove this one
},
{
"id": 7,
}
]
}
]
and this is the result I would like to obtain
[
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2,
}
]
},
{
"id": 3,
},
{
"id": 4,
"children": [
{
"id": 5,
"children": [
{
"id": 6,
}
]
}
]
},
{
"id": 7,
}
]
}
]
thank you for your reply.

I tried creating my own logic for this (probably more general than what you want), but it may help you debug your code. See the jsFiddle.
The core of the logic is
/**
* Walk through an object or array and remove duplicate elements where the 'id' key is duplicated
* Depends on a seenIds object (using it as a set)
*/
function processData(el) {
// If the element is an array...
if ($.isArray(el)) {
for (var i = 0; i < el.length; i++) {
var value = el[i];
processData(value);
// If the child is now empty, remove it from the array
if (checkForEmpty(value)) {
el.splice(i, 1);
i--; // Fix index after splicing (http://stackoverflow.com/a/9882349/1370556)
}
}
}
// If the element is an object...
else if ($.isPlainObject(el)) {
for (var key in el) {
// Make sure the key is not part of the prototype chain
if (el.hasOwnProperty(key)) {
var value = el[key];
if (key == 'id') {
// If the key has been seen, remove it
if (seenIds[value]) {
delete el[key];
continue; // Skip further processing
} else seenIds[value] = true;
}
processData(value);
// If the child is now empty, remove it from the object
if (checkForEmpty(value)) delete el[key];
}
}
}
}

Related

Remove object inside array items

I have two array of objects like so:
Main Array:
[
{
"controller": "Education",
"items": [
{
"name": "Insert",
"controller": "Education",
"id": 1
}...etc
]
},
{
"controller": "CustomerPackage",
"items": [
{
"name": "Insert",
"controller": "CustomerPackage",
"id": 1
}..etc
]
}
]
Have the selected list similar as above list.
if there are items in the selected list, I want to remove them from the main array
Here's what I've tried so far, any ideas?
this.mainArray.map(item => {
let subItemId = parseInt(item.items.map(i => i.id));
this.selectedArray.map(v => {
v.items.map(j => {
if (subItemId == j.id) {
console.log("how can i make for mainArray list ");
console.log(j.name);
}
})
})
You need to create an auxiliary variable to get the position of the item in the list you want to delete.
var myArray = [1, 2, 3, 4, 5, 6];
var positionList = -1;
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] == 4)
positionList = i;
}
myArray.splice(positionList, 1);
alert(myArray);
Try it.

Remove duplicate array from response comparing attribute value

I want to remove a duplicate array from the response on the basis of the attribute value. If the attribute_value data match with other array attribute value then other should be removed.
The logic is very simple. check duplicate attribute_value in each array and remove duplicate array and return
In response. now you can see the attribute value = 1 is thrice
and attribute value = 2 is twice
How do i compare and remove whole array if I see attribute value duplicate?
I tried with filter method which seems not working. Please help.
for(var j=0; j<social_post_link.length; j++){
newFilterarray = social_post_link[j].activity_attributes[0].attribute_value.filter(function(item, index) {
if (social_post_link[j].activity_attributes[0].attribute_value.indexOf(item) == index){
return social_post_link;
}
});
}
Response
[
{
"id": "484822",
"activity_attributes": [
{
"id": "868117",
"activity_id": "484822",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484884",
"activity_attributes": [
{
"id": "868175",
"activity_id": "484884",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484888",
"activity_attributes": [
{
"id": "868182",
"activity_id": "484888",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484823",
"activity_attributes": [
{
"id": "868120",
"activity_id": "484823",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484975",
"activity_attributes": [
{
"id": "868344",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484891",
"activity_attributes": [
{
"id": "868189",
"attribute_name": "position",
"attribute_value": "3",
}
]
},
{
"id": "484903",
"activity_attributes": [
{
"id": "868200",
"attribute_name": "position",
"attribute_value": "4",
},
]
}
]
Desired output
[
{
"id": "484822",
"activity_attributes": [
{
"id": "868117",
"activity_id": "484822",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484823",
"activity_attributes": [
{
"id": "868120",
"activity_id": "484823",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484891",
"activity_attributes": [
{
"id": "868189",
"attribute_name": "position",
"attribute_value": "3",
}
]
},
{
"id": "484903",
"activity_attributes": [
{
"id": "868200",
"attribute_name": "position",
"attribute_value": "4",
},
]
}
]
You can probably use the lodash utility uniqBy,
where iteratee is a function that returns the value you want to compare against.
In your case, it would probably look like the following:
const uniqueLinks = _.uniqBy(social_post_link, item =>
item.activity_attributes[0].attribute_value
)
Edit:
Here is a vanilla JS function that will accomplish the same.
const filterByIteratee = (array, iteratee) => {
// Empty object to store attributes as we encounter them
const previousAttributeNames = {
}
return array.filter(item => {
// Get the right value
const itemValue = iteratee(item)
// Check if we have already stored this item
if (previousAttributeNames.hasOwnProperty(itemValue)) return false
else {
// Store the item so next time we encounter it we filter it out
previousAttributeNames[itemValue] = true
return true
}
})
}
It will loop through an array, store its identifier by some function, and return only the first instance of each item.
Use it the same way:
const uniqueLinks = filterByIteratee(social_post_link, item =>
item.activity_attributes[0].attribute_value
)
This is probably not the best performing solution. but it works for your requirements.
var resultArray = [];
for (var i = 0; i < social_post_link.length; i++) {
var currentSocialLink = social_post_link[i];
for (var j = 0; j < currentSocialLink.activity_attributes.length; j++) {
if (!resultArray.some(val =>
val.activity_attributes.some(activity =>
activity.attribute_value === currentSocialLink.activity_attributes[j].attribute_value))) {
resultArray.push(currentSocialLink);
}
}
}
function removeDuplicates(myArr, prop) { // removes duplicate objects from array
return myArr.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
});
};
I found this function not too long ago which removes duplicate objects from an array. Pass it the array and the property you wish to not be duplicated.

Update Objects of Objects after Deleting Data

I am stuck in a situation where I need to update my object of objects to update , the real problem is i want to update its index with the deleted item for example:
{
"selected": null,
"lists": {
"0": [
{
"key": "Survey Meta Data"
}
],
"1": [
{
"key": "Assessment"
}
],
"2": [
{
"key": "Income Survey Process"
}
],
"3": [
{
"key": "Appeal Process"
}
]
}
}
suppose i deleted the lists[0]:
now the current items will be like:
{
"selected": null,
"lists": {
//"0": [ as deleted so no need just update the others without losing data
// {
// "key": "Survey Meta Data"
// }
//],
"0": [ //now it gets 0
{
"key": "Assessment"
}
],
"1": [//now it gets 1
{
"key": "Income Survey Process"
}
],
"2": [//now it gets 2
{
"key": "Appeal Process"
}
]
}
}
app.js
function add(data)
{
for (var i=0;i<4;i++)
{
$scope.models.lists[i]=[{"key":data[i].id}]
}
}
function remove(key)
{
for (var i=0;i<$scope.len;i++)
{
for (var s=0;s<$scope.models.lists[i].length;s++)
{
if(key==$scope.models.lists[i][s].key) {
delete $scope.models.lists[i]// deleted now want to update
break;
}
}
}
}
Quite simple. Dont delete the wanted but the last, and copy one position backward after the element you want to delete:
function remove(key){
var found=false;
var list=$scope.models.lists;
//get the length to properly iterate, requires proper keys e.g. 0,1,2,3
var length=+Object.keys(list).sort().reverse()[0]+1;
//iterate over
for (var i=0;i<length-1;i++){
if(key===list[i][0].key || found) {
//if found override current with next
found=true;
list[i]=list[i+1];
}
}
//delete last one if weve found sth before, or if the last is the one to be deleted
if(list[i][0].key===key || found){
delete list[i];
}
}
http://jsbin.com/casikuxoha/edit?console
or with multiple:
http://jsbin.com/tewaveyiwa/1/edit?console
In my previous answer I was completely oblivious of your needs.
Here is the code that allows you to delete any item of the list (copy/paste this in a pristine component and look at the console log) :
list = {
'selected': null,
lists: {
'0': [{ 'key': 'Survey Meta Data' }],
'1': [{ 'key': 'Assessment' }],
'2': [{ 'key': 'Income Survey Process' }],
'3': [{ 'key': 'Appeal Process' }]
}
};
constructor() { }
ngOnInit() {
this.removeItem('1');
}
removeItem(index: string) {
let keys = Object.keys(this.list.lists);
keys = keys.filter(k => k !== index);
let myNewVar = { selected: null, lists: {} };
for (let key of keys) { myNewVar.lists[key] = this.list.lists[key]; }
console.log(myNewVar);
}

Recursive function not returning Label in javascript

I'm trying to return the Label match however I seem to be doing something wrong here. Can someone push me in the right direction?
console.log('start');
var test = {
"ID": 234324,
"Label": "KDF",
"children": [{
"ID": 234234,
"Label": "KJDF",
"children": [{
"ID": 234324,
"Label": "KJDF"
}, {
"ID": 22323,
"Label": "LKNDF"
}, {
"ID": 34535,
"Label": "LKNSF"
}]
}, {
"ID": 323434,
"Label": "CLK"
}]
}
function testThis(thing, ID) {
if (thing.ID == ID) {
console.log('match!')
return thing.Label;
} else if (thing.children && thing.children.length) {
thing.children.forEach(function(x) {
console.log(x);
return testThis(x, ID);
})
return false;
} else {
return false;
}
}
console.log(testThis(test, 323434));
console.log('end');
Where you do
thing.children.forEach(function(x) {
use .some() instead of .forEach(), like this
return thing.children.some(function(x) {})
.forEach() returns undefined, while .some() will return either true or false and will stop iterating once trueis returned.
some() executes the callback function once for each element present in the array until it finds one where callback returns a true value. If such an element is found, some() immediately returns true. Otherwise, some() returns false.
Fiddle: https://jsfiddle.net/mkarajohn/7ebr6pna/
You don't need to use foreach for that, use a normal for like this
function testThis(thing, ID) {
if (thing.ID == ID) {
return thing.Label;
} else if (thing.children && thing.children.length) {
var label;
var length = thing.children.length
for(var i = 0; i < length; i++) {
label = testThis(thing.children[i], ID);
if(label) {
return label;
}
}
}
return false;
}
You're expecting forEach to return something and leave the loop. It always returns undefined and always iterates every element.
forEach() executes the callback function once for each array element;
unlike map() or reduce() it always returns the value undefined and is
not chainable. The typical use case is to execute side effects at the
end of a chain.
See MDN forEach
The Array.prototype.some() might serve you better since it will only execute some of the elements and returns a value.
See working code below:
console.log('start');
var test = {
"ID": 1,
"Label": "A",
"children": [{
"ID": 2,
"Label": "B",
"children": [{
"ID": 5,
"Label": "E"
}, {
"ID": 6,
"Label": "F"
}, {
"ID": 7,
"Label": "G"
}]
}, {
"ID": 3,
"Label": "C"
}, {
"ID": 4,
"Label": "D",
"children": [{
"ID": 8,
"Label": "H"
}, {
"ID": 9,
"Label": "I"
}]
}]
}
function testThis(thing, ID) {
if (thing.ID == ID) {
console.log('match!')
return thing.Label;
} else if (thing.children && thing.children.length) {
var theone = null;
thing.children.some(function(x) {
theone = testThis(x, ID);
return theone;
})
return theone;
} else {
return false;
}
}
alert(testThis(test, 5));
console.log('end');

JSON Data Fuzzy merge

I have a JSON data like this
{
"array": {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"PARENT": [
{
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}
]
},
"InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
}
]
}
}
}
I need to search for fuzzy data and merge. For example InvestmentsDeposits and InvestmentsDeposits$$$d need to be merged because it matches closely in name
Need to use javascript for this
For now I can make sure source data will always have $$$d at the end to merge with the target data without $$$d i.e., InvestmentDeposits.
My final merged content should be like this
{
"array": {
"InvestmentsDeposits": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
"text": "newtext"
}
]
}
}
}
any help on this one?
What I have tried so far
var json0 = {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"PARENT": [
{
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}
]
}
};
var json1 =
{
"InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"PARENT": [
{
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
}
]
}
};
// Merge object2 into object1, recursively
$.extend( true, json0, json1 );
I am able to merge the data if i am able to split the InvestmentDeposits and InvestmentDeposits$$$d in to two distinct JSON objects but how to split and move the $$$d data in to another object? to make the jquery extend work
Use Object.keys() to find an object's keys and figure out what data to move over. You can compare the first key with the others to find matches, then remove the keys you just looked at until all of them are gone. Here's an example with a similar object.
var dat = {
"InvestmentsDeposits": {
"NAME": "Investments & Deposits",
"CONTENT_ID": "Promotions",
"DISPLAY_ORDER": 3,
"PATH": "/Promotions"
}, "InvestmentsDeposits$$$d": {
"NAME": "Deposits",
"CONTENT_ID": "NewPromotion",
"text" : "newtext"
},
"NotLikeTheOthers": {
"Um": "Yeah."
}
};
var result = {}; // This will be the merged object
var keys = Object.keys(dat); // Contains keys
while(keys.length) {
var i=1;
for(; i<keys.length; i++) { // Find matches
if(keys[0] == keys[i] + '$$$d') { // Match type 1
result[keys[i]] = dat[keys[i]]; // Copy orig
for(var j in dat[keys[0]]) { // Replace values
result[keys[i]][j] = dat[keys[0]][j];
}
keys.splice(i,1);
keys.shift();
i = 0;
break;
} else if(keys[i] == keys[0] + '$$$d') { // Reverse matched
result[keys[0]] = dat[keys[0]];
for(var j in dat[keys[i]]) {
result[keys[0]][j] = dat[keys[i]][j];
}
keys.splice(i,1);
keys.shift();
i = 0;
break;
}
}
if(i > 0) { // Didn't find a match
result[keys[0]] = dat[keys[0]];
keys.shift();
}
}
alert(JSON.stringify(result));
Note that Object.keys() requires IE9+.

Categories

Resources