Srinivasan Raman
Srinivasan Raman

Reputation: 461

Want to convert a nested object to query parameter for attaching to url

Hi I need to construct to url request with query parameter, I have a nested object with the key and values, like below

 "user": {
    "first_name": "Srini",
    "last_name": "Raman",
     "gender": "male",
     "dob": "1992-08-02",
     "address_attributes": {
      "city": "San Diego",
      "state": "CA",
      "zip": 92127,
      "country": "USA",
      "latitude": 37.257009,
      "longitude": -120.050767
    }
}


i need to get a query parameter like

user[first_name]=Srini&user[last_name]=Raman&user[address_attributes][city]=San Diego&user[address_attributes][state]=CA

Upvotes: 10

Views: 20177

Answers (4)

converted [fr1sk answer] to vanilla js. (no ts & no lodash requirement) also changed encodeURI to encodeURIComponent as suggested by Alex:

The encodeURIComponent() function encodes a URI by replacing each instance of certain characters ... Compared to encodeURI(), this function encodes more characters, including those that are part of the URI syntax.

function objectToQueryStringHelper(object, path, result) {
    return Object.entries(object).reduce((acc, [key, value]) => {
        if ((value != null) || (value?.length != 0)) {
            typeof value === 'object'
                ? acc.push(
                      ...objectToQueryStringHelper(
                          value,
                          [...path, key],
                          result
                      )
                  )
                : acc.push({ keyPath: [...path, key], value });
        }
        return acc;
    }, []);
}

export function objectToQueryString(object) {
    const simplifiedData = objectToQueryStringHelper(object, []);
    const queryStrings = simplifiedData.map(
        ({ keyPath: [firstKey, ...otherKeys], value }) => {
            const nestedPath = otherKeys.map((key) => `[${key}]`).join("");
            return `${firstKey}${nestedPath}=${
                (value != null) ? encodeURIComponent(`${value}`) : ""
            }`;
        }
    );
    return queryStrings.join("&");
}

Upvotes: 1

Franck MARIN
Franck MARIN

Reputation: 71

You can use the qs package.

Their stringify method seems to do exactly what you want.

Usage example:

window.addEventListener("load", () => {
  const obj = {
    "user": {
      "first_name": "Srini",
      "last_name": "Raman",
      "gender": "male",
      "dob": "1992-08-02",
      "address_attributes": {
        "city": "San Diego",
        "state": "CA",
        "zip": 92127,
        "country": "USA",
        "latitude": 37.257009,
        "longitude": -120.050767
      }
    }
  };

  console.log(Qs.stringify(obj, { encode: false }));
}, {
  once: true
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.11.0/qs.min.js"></script>

Upvotes: 6

fr1sk
fr1sk

Reputation: 321

Here is a TypeScript implementation if someone needs it.


const testObject = {
  "user": {
    "first_name": "Srini",
    "last_name": "Raman",
    "gender": "male",
    "dob": "1992-08-02",
    "address_attributes": {
      "city": "San Diego",
      "state": "CA",
      "zip": 92127,
      "country": "USA",
      "latitude": 37.257009,
      "longitude": -120.050767
    }
  }
}

interface ObjectToQueryStringHelperObject {
  keyPath: Array<string>;
  value: boolean | number | string;
}

function objectToQueryStringHelper(
  object: any,
  path: Array<string> = [],
  result: Array<ObjectToQueryStringHelperObject> = []
): Array<ObjectToQueryStringHelperObject> {
  return Object.entries(object).reduce((acc, [key, value]) => {
    if (!_.isNil(value) || !_.isEmpty(value)) {
      _.isObject(value) ?
        acc.push(...objectToQueryStringHelper(value, [...path, key], result)) :
        acc.push({ keyPath: [...path, key], value });
    }
    return acc;
  }, []);
}

export function objectToQueryString(object: any): string {
  const simplifiedData = objectToQueryStringHelper(object);
  const queryStrings = simplifiedData.map(({ keyPath: [firstKey, ...otherKeys], value }) => {
    const nestedPath = otherKeys.map(key => `[${key}]`).join('');
    return `${firstKey}${nestedPath}=${!_.isNil(value) ? encodeURI(`${value}`) : ''}`
  })
  return queryStrings.join('&');
}

console.log(objectToQueryString(testObject))

Upvotes: 3

junvar
junvar

Reputation: 11574

let obj = {
  user: {
    first_name: 'Srini',
    last_name: 'Raman',
    gender: 'male',
    dob: '1992-08-02',
    address_attributes: {
      city: 'San Diego',
      state: 'CA',
      zip: 92127,
      country: 'USA',
      latitude: 37.257009,
      longitude: -120.050767
    }
  }
};

let getPairs = (obj, keys = []) =>
  Object.entries(obj).reduce((pairs, [key, value]) => {
    if (typeof value === 'object')
      pairs.push(...getPairs(value, [...keys, key]));
    else
      pairs.push([[...keys, key], value]);
    return pairs;
  }, []);

let x = getPairs(obj)
  .map(([[key0, ...keysRest], value]) =>
    `${key0}${keysRest.map(a => `[${a}]`).join('')}=${value}`)
  .join('&');
console.log(x);

Upvotes: 17

Related Questions