0

I have an object where at global level I have the changed values and inside one property called initialData I have the initial value, so what I am trying to do is, based on the array mentioned values I need to find whether the initialData has been changed or not.

const eligibleFields = ['address_line_1', "is_new"]

const object = {
  "id": "1",
  "isGetting": false,
  "address_line_1": "Washington DC",
  "address_line_2": "Newyork",
  "isOpen": false,
  "comment": "Changed Data",
  "initialData": {
      "id": 1,
      "is_new": true,
      "address": {
          "address_line_1": "Washington",
          "address_line_2": "Newyork",
      },
      "comment": "Initial Data"
  }
}

Here first I need to loop through the mentioned fields like address_line_1 and take the value as Washington, now compare it outer not inside initialData, so outer its Washington DC, so there is a change.

Now I need to return a boolean value.

This is working, but is there a simple way?

const eligibleFields = ['address_line_2', "is_new"]

const object = {
  "id": "1",
  "isGetting": false,
  "address_line_1": "Washington DC",
  "address_line_2": "Newyork",
  "isOpen": false,
  "comment": "Changed Data",
  "initialData": {
    "id": 1,
    "is_new": true,
    "address": {
      "address_line_1": "Washington",
      "address_line_2": "Newyork",
    },
    "comment": "Initial Data"
  }
}


function findVal(obj, key) {
  var seen = new Set,
    active = [obj];
  while (active.length) {
    var new_active = [],
      found = [];
    for (var i = 0; i < active.length; i++) {
      Object.keys(active[i]).forEach(function(k) {
        var x = active[i][k];
        if (k === key) {
          found.push(x);
        } else if (x && typeof x === "object" &&
          !seen.has(x)) {
          seen.add(x);
          new_active.push(x);
        }
      });
    }
    if (found.length) return found;
    active = new_active;
  }
  return null;
}

let isChanged = eligibleFields.some(field => {
  let initialValue = findVal(object.initialData, field)?.[0]
  if (initialValue) {
    let changedValue = findVal(object, field)?.[0]
    if (changedValue != initialValue) {
      console.log("changedValue =>",changedValue, ",",  "initialValue =>",initialValue)
      return true
    }
  }
})

console.log(isChanged)
iLuvLogix
  • 5,920
  • 3
  • 26
  • 43
dev
  • 814
  • 10
  • 27
  • 1
    Why both of your object are not following the same json structure? I mean in outer json you have `address_line_1` on the root, but inside the `initialData`, you have the same key nested inside `address`. – Raghav Garg Jan 27 '22 at 16:40
  • Yep thats the structure so thats why recursively checking anywhere – dev Jan 27 '22 at 16:42
  • This question is a possible duplicate for, https://stackoverflow.com/questions/15523514/find-by-key-deep-in-a-nested-array – Raghav Garg Jan 27 '22 at 16:43
  • 1
    I agree with @RaghavGarg at least both should have same structure – Andam Jan 27 '22 at 16:45
  • You can solve it by recursive search but it should not be like that, you should keep both the json structure the same, so you can easily go directly to the key in O(1) time and compare the values directly. – Raghav Garg Jan 27 '22 at 16:47
  • @dev do you have duplicate keys inside initialData. like do you have for example name: "a", car:{name: "aa"} as you can see name key is repeated – Andam Jan 27 '22 at 16:48

2 Answers2

0

This seems simpler to me:

const eligibleFields = ['address_line_1', "is_new"]

const object = {
  "id": "1",
  "isGetting": false,
  "address_line_1": "Washington DC",
  "address_line_2": "Newyork",
  "isOpen": false,
  "comment": "Changed Data",
  "initialData": {
    "id": 1,
    "is_new": true,
    "address": {
      "address_line_1": "Washington",
      "address_line_2": "Newyork",
    },
    "comment": "Initial Data"
  }
}

const recursiveSearch = (obj, searchKey) => {
  for (const [key, value] of Object.entries(obj)) {
    if (key === searchKey && typeof value !== 'object') {
      return value;
    } else if (typeof value === 'object') {
      return recursiveSearch(value, searchKey);
    }
  }

  return undefined;
};

let isChanged = eligibleFields.some(field => {
  if (!(field in object)) return false;
  
  let initialValue = recursiveSearch(object.initialData, field);
  let changedValue = object[field];
  
  if (changedValue !== initialValue) {
    console.log(`"${field}" was changed from ${initialValue} to ${changedValue}`)
    return true
  }
})

console.log(isChanged)

This assumes that only initialData has nested values, as the example provided implies. Anyways you should consider changing your JSON schema to match the initial data and the changed data.

Alvaro Flaño Larrondo
  • 5,516
  • 2
  • 27
  • 46
0

If initialData does not have duplicate key names inside, I suggest to flaten the initialData first into an array of key value pairs so you can access it later by key names.

In below code first we are doing the conversion from this

{
  "id": 1,
  "is_new": true,
  "address": {
    "address_line_1": "Washington",
    "address_line_2": "Newyork"
  },
  "comment": "Initial Data"
}

to this

{
  "id": 1,
  "is_new": true,
  "address_line_1": "Washington",
  "address_line_2": "Newyork",
  "comment": "Initial Data"
}

by this code

function flatenInitialData(obj, res = {}){
    for(let key in obj){
        if(typeof obj[key] == 'object'){
            flatenInitialData(obj[key], res);
        } else {
            res[key] = obj[key];
        }
    }
    return res;
}

which goes into deepest level and takes the key value and adds it into a new key and value pair array

finally we just do one for loop only for eligibleFields which loops 2 times

Please note in below example if eligibleFields has a entry such as "is_new" but "is_new" is not in the outer array it will still count it as not changed but you can change this in the condition part inside the for loop code

const eligibleFields = ['address_line_2', "is_new"]

const object = {
  "id": "1",
  "isGetting": false,
  "address_line_1": "Washington DC",
  "address_line_2": "Newyork",
  "isOpen": false,
  "comment": "Changed Data",
  "initialData": {
    "id": 1,
    "is_new": true,
    "address": {
      "address_line_1": "Washington",
      "address_line_2": "Newyork",
    },
    "comment": "Initial Data"
  }
}

function flatenInitialData(obj, res = {}){
    for(let key in obj){
        if(typeof obj[key] == 'object'){
            flatenInitialData(obj[key], res);
        } else {
            res[key] = obj[key];
        }
    }
    return res;
}
var flatInitialData = flatenInitialData(object["initialData"]);


function isChanged(){
  var hasChanged = false;
  for(var i = 0; i < eligibleFields.length; i++){
    if(object[eligibleFields[i]]){
      if(object[eligibleFields[i]] != flatInitialData[eligibleFields[i]]){
        hasChanged = true;
      }
    }
  }
  
  return hasChanged;
}


console.log(isChanged())
Andam
  • 2,087
  • 1
  • 8
  • 21