4

I've searched for a long time, but cannot find any solution for my problem... I got a random nested json object like this:

var obj = { a: 1, b: 2, c: { a: 1, b: 2, c: {a: 'hello', b: 'HowAreYou?'} } };
someValue = 'hello';
newValue = 'bye';

Now I'm going to search for 'someValue'. If 'obj' contains this value the alert will be triggered. Here's my example:

function wannaChangeValue(obj) {
    var itemKey;
    if (obj instanceof Object) {
        for (itemKey in obj) {
            if (obj.hasOwnProperty(itemKey)) {
                wannaChangeValue(obj[itemKey]);
            }
        }
    } else {
        alert(obj);
        if (obj == someValue) {
            alert('got it! now change...')
            //change someValue to newValue 
        }
    }
    return obj
}

wannaChangeValue(obj)

This works really fine, but how can I change 'someValue' to 'newValue' and return the whole json-file again? I've seen many examples how to handle this, but I need a solution for ANY kind of nested json object WITHOUT knowing the path for changing this value. Maybe this is a completely wrong approach... Thanks in advance!

Endivie
  • 101
  • 1
  • 2
  • 8
  • Possible duplicate of [Find and update in nested json object](https://stackoverflow.com/questions/17988939/find-and-update-in-nested-json-object). Another link that might help: https://stackoverflow.com/questions/15523514/find-by-key-deep-in-a-nested-object – Rajesh Aug 03 '17 at 09:58

6 Answers6

4

You could use a recursice approach for any found object, call the function again.

function update(object, search, replace) {
    Object.keys(object).forEach(function (k) {
        if (object[k] && typeof object[k] === 'object') {
            return update(object[k], search, replace)
        }
        if (object[k] === search) {
            object[k] = replace;
        }
    });
    
}

var object = { a: 1, b: 2, c: { a: 1, b: 2, c: {a: 'hello', b: 'HowAreYou?'} } };
    
update(object, 'hello', 'bye');

console.log(object)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • What is the purpose of this: `object[k] &&`? – Rajesh Aug 03 '17 at 10:13
  • it prevents `null` values to gets iterated by keys. – Nina Scholz Aug 03 '17 at 10:14
  • I think, my comment on @Alexandru-Ionut Mihai's answer would not only solve your usecase, but will also optimize as there should be no need to traverse node tree that does not have the key – Rajesh Aug 03 '17 at 10:16
  • 2
    hm. yes and no. yes, because it shortens the iteration and no, because of the overhead for every nested object. – Nina Scholz Aug 03 '17 at 10:19
  • I don't think you need `return` in the first `if` condition. Is there any specific reason you are using it? – Hassan Imam Aug 03 '17 at 10:22
  • @Rajesh, with stringified object, you could search directly with left colon and quote, seach string and another quote and replace that, without iterating the object, which i do not recommend – Nina Scholz Aug 03 '17 at 10:24
  • 3
    @HassanImam, it prevents the next `if` clause to be evaluated. – Nina Scholz Aug 03 '17 at 10:25
  • @NinaScholz Even I do not recommend as that would remove any functions and non-enumerable properties. Idea was, for big nested JSON structure, if a branch do not have search query, we should not iterate over it. – Rajesh Aug 03 '17 at 10:40
2

You can create function using for...in loop and if current value match oldValue change that value.

var obj = { a: 1, b: 2, c: { a: '1', b: 2, c: {a: 'hello', b: 'HowAreYou?'} } };
var someValue = 'hello';
var newValue = 'bye';

function changeVal(data, oldV, newV) {
  for(var i in data) {
    if(typeof data[i] == 'object') changeVal(data[i], oldV, newV);
    if(data[i] == oldV) data[i] = newV
  }
  return data
}

console.log(changeVal(obj, someValue, newValue))
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
0

You can define a function which search recursively the value through your object and then replace this value.

function replaceObj (obj, valueForSearch,valueForReplace) {
    for (var key in obj) {
        var value = obj[key];
        if (typeof value === 'object') {
            replaceObj(value, valueForSearch,valueForReplace);
        }
        if (value === valueForSearch) {
            obj[key]=valueForReplace;
        }
    }
}
let obj = { a: 1, b: 2, c: { a: 1, b: 2, c: {a: 'hello', b: 'HowAreYou?'} } };
let someValue = 'hello';
let newValue = 'bye';
replaceObj(obj,someValue,newValue);
console.log(obj);
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
  • 1
    You can even optimize it as `typeof value === 'object' && JSON.stringify(value).indexOf(query)>=0` – Rajesh Aug 03 '17 at 09:55
0

This is a quick solution:

function wannaChangeValue(obj) {
    var itemKey;
    if (obj instanceof Object) {
        for (itemKey in obj) {
            if (obj.hasOwnProperty(itemKey)) {
                obj[itemKey] = wannaChangeValue(obj[itemKey]);
            }
        }
    } else {
        alert(obj);
        if (obj == someValue) {
            alert('got it! now change...')
            //change someValue to newValue
            obj = someValue;
        }
    }
    return obj
}

wannaChangeValue(obj)

Obviously this will mutate the value on the object itself, so if you want an entirely new object you will have to do something different.

Matthew Lewis
  • 453
  • 6
  • 12
0

You could use recursive call for your object.

Demo

var object = { a: 1, b: 2, c: { a: '1', b: 2, c: {a: 'hello', b: 'HowAreYou?'} } },
  someValue = 'hello',
  newValue = 'bye';

function wannaChangeValue(obj) {
  var itemKey;
  if (obj instanceof Object) {
    for (itemKey in obj) {
      if (obj.hasOwnProperty(itemKey)) {
        if (!(obj[itemKey] instanceof Object) && obj[itemKey] == someValue) {
          //alert(someValue + ' got it! now change...')
          //change someValue to newValue
          obj[itemKey] = newValue;
          break;
        } else {
          wannaChangeValue(obj[itemKey]);
        }
      }
    }
  }
}
wannaChangeValue(object);
console.log(object)
.as-console-wrapper {max-height: 100% !important;top: 0;}
Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
  • *Dude you have to just need to some change your code like below code it will work for you.* This is a comment line, which would be flagged as *Too Chatty*. An answer should have an Explanation. – Rajesh Aug 03 '17 at 10:14
0

Here is a solution using object-scan

// const objectScan = require('object-scan');

const obj = { a: 1, b: 2, c: { a: 1, b: 2, c: { a: 'hello', b: 'HowAreYou?' } } };

const replace = (input, oldValue, newValue) => objectScan(['**'], {
  abort: true,
  rtn: 'bool',
  filterFn: ({ value, parent, property }) => {
    if (value === oldValue) {
      parent[property] = newValue;
      return true;
    }
    return false;
  }
})(input);

console.log(replace(obj, 'hello', 'bye')); // returns true iff replace happened
// => true
console.log(obj);
// => { a: 1, b: 2, c: { a: 1, b: 2, c: { a: 'bye', b: 'HowAreYou?' } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.7.1"></script>

Disclaimer: I'm the author of object-scan

vincent
  • 1,953
  • 3
  • 18
  • 24