tfontain
tfontain

Reputation: 56

How to recursively pass a function to every property of an object

Let's take this object :

"date": "1983-09-24",
"values": [
    {
        "shares": 500,
        "accountType": 1,
    },
    {
        "options": {
            "hey": "this is a string"
        }
    }
],
"comments": "Lorem ipsum dolor sit amet"

I need to pass to every property, including elements of all arrays recursively, a function. Let's say this one :

property => {
    return typeof object === 'string' ? toUpperCase(property) : property
}

So the final object would look like :

"date": "1983-09-24",
"values": [
    {
        "shares": 500,
        "accountType": 1,
    },
    {
        "options": {
            "hey": "THIS IS A STRING"
        }
    }
],
"comments": "LOREM IPSUM DOLOR SIT AMET"

How do I pass this function to every property ?

Upvotes: 1

Views: 599

Answers (5)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48751

You can recursively call the function below if they incoming value is an array or the value is not a string. If it is a string, assign the value as the uppercase version.

This modifies the object in-place.

const obj = {
  "date": "1983-09-24",
  "values": [{
    "shares": 500,
    "accountType": 1,
  }, {
    "options": {
      "hey": "this is a string"
    }
  }],
  "comments": "Lorem ipsum dolor sit amet"
};

const isObject = obj => typeof obj === 'object' && obj !== null;

const valuesToUpperCase = (obj) => {
  if (isObject(obj)) {
    Object.entries(obj).forEach(([key, value]) => {
      if (typeof value === 'string') {
       obj[key] = value.toUpperCase();
      } else {
        valuesToUpperCase(value);
      }
    });
  } else if (Array.isArray(obj)) {
    obj.forEach(item => valuesToUpperCase(item));
  }
};

valuesToUpperCase(obj);
console.log(obj);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Here is a version that allows you to pass a callback:

const obj = {
  "date": "1983-09-24",
  "values": [{
    "shares": 500,
    "accountType": 1,
  }, {
    "options": {
      "hey": "this is a string"
    }
  }],
  "comments": "Lorem ipsum dolor sit amet"
};

const transformValues = (obj, fn) => {
  if (Array.isArray(obj))
    obj.forEach(item => transformValues(item, fn));
  else if (typeof obj === 'object')
    Object.entries(obj).forEach(([key, value]) => {
      if (typeof value === 'string') obj[key] = fn(value);
      else transformValues(value, fn);
    });
};

transformValues(obj, str => str.toUpperCase());
console.log(obj);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Upvotes: 0

Daniel Lord
Daniel Lord

Reputation: 792

here you go

let a =
{
  "date": "1983-09-24",
  "values": [
      {
          "shares": 500,
          "accountType": 1,
      },
      {
          "options": {
              "hey": "this is a string"
          }
      }
  ],
  "comments": "Lorem ipsum dolor sit amet"
}

function upperAllProps(inObj){
  for (const [key, value] of Object.entries(inObj)) {
    if(typeof value === 'string') { inObj[key] = value.toUpperCase();}
    else if(typeof value === 'object') {upperAllProps(value);}
  }  
}

console.log(a);
upperAllProps(a);
console.log(a);

Upvotes: 0

ray
ray

Reputation: 27295

You could use reduce with a recursive transformation function to walk all the values:

const input = {
  "date": "1983-09-24",
  "values": [
    {
        "shares": 500,
        "accountType": 1,
    },
    {
        "options": {
            "hey": "this is a string"
        }
    }
  ],
  "comments": "Lorem ipsum dolor sit amet"
}

// Utility function for transforming non-object values.
// If value has a toUpperCase function, invoke it, otherwise return the input value.
const transformValue = value => value?.toUpperCase?.() ?? value;

// Recursively transform the object's properties
const transformObject = obj => Object.entries(obj).reduce((acc, [key, value]) => {
  acc[key] = typeof value === 'object'
    ? transformObject(value) // value is an object. call transformObject on it too.
    : transformValue(value); // not an object. transform the value.
  return acc;
}, {});

const output = transformObject(input);

console.log(output);

Upvotes: 0

georg
georg

Reputation: 215059

Like this?

function mapRec(x, fn) {
    if (Array.isArray(x))
        return x.map(v => mapRec(v, fn));
    if (x && typeof x === 'object')
        return Object.fromEntries(Object.entries(x).map(([k, v]) => [k, mapRec(v, fn)]));
    return fn(x);
}


//

obj = {
    "date": "1983-09-24",
    "values": [
        {
            "shares": 500,
            "accountType": 1,
        },
        {
            "options": {
                "hey": "this is a string"
            }
        },
    ],
    "comments": "Lorem ipsum dolor sit amet"
}


res = mapRec(obj, x => x.toUpperCase ? x.toUpperCase() : x)

console.log(res)

Upvotes: 2

Gunther
Gunther

Reputation: 1328

Use an algorithm that recursively iterates the object properties and call the str.toUpperCase() function to all strings found. Working example:

const input = {
  date: "1983-09-24",
  values: [{
      shares: 500,
      accountType: 1,
    },
    {
      options: {
        "hey": "this is a string"
      }
    }
  ],
  comments: "Lorem ipsum dolor sit amet"
}

function convertToUpperCase(obj) {
  for (key in obj) {
    if (typeof obj[key] === 'object' && obj != null)
      convertToUpperCase(obj[key]);
    else if (typeof obj[key] === 'string')
      obj[key] = obj[key].toUpperCase();
  }
  return obj;
}

console.log(convertToUpperCase(input));

Upvotes: 0

Related Questions