5

I have a sanitizeStr() function that I need to run on EVERY property/subproperty that exists in an object like the one below:

const data = {
  info: 'schools',
  schools: [
    { name: 'Johnson Elementary', type: 'elementary' },
    { name: 'Iselin Middle School', type: 'middle' }
  ],
  bestStudent: {
    name: 'John',
    grade: 'sixth'
  }
};

The issue is that for every single one of these properties, they may or may not exist. Right now, I'm having to do multiple if checks for each property and manually running the function:

// Is there a better way to do this rather than what I have here:

if (data.info) {
  data.info = sanitizeStr(data.info);
}

if (data.bestStudent) {
  if (data.bestStudent.name) {
    data.bestStudent.name = sanitizeStr(data.bestStudent.name);
  }

  if (data.bestStudent.grade) {
    data.bestStudent.grade = sanitizeStr(data.bestStudent.grade);
  }
}

if (data.schools) {
  data.schools.forEach((school, i) => {
    if (school.name) {
      data.schools[i].name = sanitizeStr(school.name);
    }

    if (school.grade) {
      data.schools[i].grade = sanitizeStr(school.grade);
    }
  });
}

If anyone knows of a cleaner/less manual way of doing this, it would be appreciated.

Saad
  • 49,729
  • 21
  • 73
  • 112
  • what is `sanitizeStr` doing? please add the function as well. – Nina Scholz Mar 27 '17 at 21:21
  • I didn't include it because I don't think it's quite relevant – It just does some string replacements. I'm basically just trying to figure out a better way to run that function on every property without having to do it one by one manually. – Saad Mar 27 '17 at 21:23
  • Possible duplicate of [Iterate through object properties](http://stackoverflow.com/questions/8312459/iterate-through-object-properties) – Heretic Monkey Mar 27 '17 at 21:23
  • @MikeMcCaughan I'm aware of how to iterate through an object's properties. However, I can't just do a loop and run `sanitizeStr` on each prop in the loop because the property might be a string, an object, or an array of objects. – Saad Mar 27 '17 at 21:24
  • And you can't check the type before running `sanitizeStr` on a property? `typeof stringVar === 'string'`... – Heretic Monkey Mar 27 '17 at 21:27
  • Well if the type is not a string, but an object, I want to traverse through that object as well and if its an array of objects I want to traverse through that array and traverse through each prop of each object in the array. So it's a bit more complicated than that. Nina's answer looks good tho – Saad Mar 27 '17 at 21:36

2 Answers2

8

You could use an iterative and recursive approach for objects and call the function for non objects only.

function sanitizeStr(s) {
    return '#' + s;
}

function iterAll(object) {
    Object.keys(object).forEach(function (k) {
        if (object[k] && typeof object[k] === 'object') {
            iterAll(object[k]);
            return;
        }
        object[k] = sanitizeStr(object[k]);
    })
}

var data = { info: 'schools', schools: [{ name: 'Johnson Elementary', type: 'elementary' }, { name: 'Iselin Middle School', type: 'middle' }], bestStudent: { name: 'John', grade: 'sixth' } };

iterAll(data);

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    You may want to check for strings explicitly in order to avoid sanitizing numbers (thereby converting them to strings). +1 in any case. – Rick Hitchcock Mar 27 '17 at 21:32
  • 1
    At this point in my code, if the properties DO exist they are guaranteed to be strings, but that's a useful comment in case anyone else sees this down the line. And thanks a lot, I think I can go with an approach like this. – Saad Mar 27 '17 at 21:35
0

You must me looking for this

const sanitizeObject = (obj, callBack, isClone = false) => {
    let tempObj = obj;
    if(typeof callBack === 'function' && (typeof tempObj === 'string' || typeof tempObj === 'number')){
      return callBack(tempObj)
    }else if(typeof tempObj === 'object' && tempObj !== null){
      tempObj = isClone ? (Array.isArray(tempObj) ? [...tempObj] : {...tempObj}) : tempObj;
      Object.keys(tempObj).forEach(objKey => {
        const valueOfobject = tempObj[objKey]
        if(typeof valueOfobject === 'string' || typeof valueOfobject === 'number'){
          tempObj[objKey] = callBack(tempObj[objKey])
        }else {
          tempObj[objKey] = sanitizeObject(valueOfobject, callBack, isClone)
        }
      })
    } 
    return tempObj;
}

const data = {
test1: {
  test2: [{
  property: "any string",
  property2: null
}]}
}

console.log(sanitizeObject(data, function (stringValue){
  return stringValue + " apend"
}))