498

I have a string like this:

abc=foo&def=%5Basf%5D&xyz=5

How can I convert it into a JavaScript object like this?

{
  abc: 'foo',
  def: '[asf]',
  xyz: 5
}
3

34 Answers 34

1
2
0

Using phpjs

function parse_str(str, array) {
  //       discuss at: http://phpjs.org/functions/parse_str/
  //      original by: Cagri Ekin
  //      improved by: Michael White (http://getsprink.com)
  //      improved by: Jack
  //      improved by: Brett Zamir (http://brett-zamir.me)
  //      bugfixed by: Onno Marsman
  //      bugfixed by: Brett Zamir (http://brett-zamir.me)
  //      bugfixed by: stag019
  //      bugfixed by: Brett Zamir (http://brett-zamir.me)
  //      bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
  // reimplemented by: stag019
  //         input by: Dreamer
  //         input by: Zaide (http://zaidesthings.com/)
  //         input by: David Pesta (http://davidpesta.com/)
  //         input by: jeicquest
  //             note: When no argument is specified, will put variables in global scope.
  //             note: When a particular argument has been passed, and the returned value is different parse_str of PHP. For example, a=b=c&d====c
  //             test: skip
  //        example 1: var arr = {};
  //        example 1: parse_str('first=foo&second=bar', arr);
  //        example 1: $result = arr
  //        returns 1: { first: 'foo', second: 'bar' }
  //        example 2: var arr = {};
  //        example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', arr);
  //        example 2: $result = arr
  //        returns 2: { str_a: "Jack and Jill didn't see the well." }
  //        example 3: var abc = {3:'a'};
  //        example 3: parse_str('abc[a][b]["c"]=def&abc[q]=t+5');
  //        returns 3: {"3":"a","a":{"b":{"c":"def"}},"q":"t 5"}

  var strArr = String(str)
    .replace(/^&/, '')
    .replace(/&$/, '')
    .split('&'),
    sal = strArr.length,
    i, j, ct, p, lastObj, obj, lastIter, undef, chr, tmp, key, value,
    postLeftBracketPos, keys, keysLen,
    fixStr = function(str) {
      return decodeURIComponent(str.replace(/\+/g, '%20'));
    };

  if (!array) {
    array = this.window;
  }

  for (i = 0; i < sal; i++) {
    tmp = strArr[i].split('=');
    key = fixStr(tmp[0]);
    value = (tmp.length < 2) ? '' : fixStr(tmp[1]);

    while (key.charAt(0) === ' ') {
      key = key.slice(1);
    }
    if (key.indexOf('\x00') > -1) {
      key = key.slice(0, key.indexOf('\x00'));
    }
    if (key && key.charAt(0) !== '[') {
      keys = [];
      postLeftBracketPos = 0;
      for (j = 0; j < key.length; j++) {
        if (key.charAt(j) === '[' && !postLeftBracketPos) {
          postLeftBracketPos = j + 1;
        } else if (key.charAt(j) === ']') {
          if (postLeftBracketPos) {
            if (!keys.length) {
              keys.push(key.slice(0, postLeftBracketPos - 1));
            }
            keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos));
            postLeftBracketPos = 0;
            if (key.charAt(j + 1) !== '[') {
              break;
            }
          }
        }
      }
      if (!keys.length) {
        keys = [key];
      }
      for (j = 0; j < keys[0].length; j++) {
        chr = keys[0].charAt(j);
        if (chr === ' ' || chr === '.' || chr === '[') {
          keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
        }
        if (chr === '[') {
          break;
        }
      }

      obj = array;
      for (j = 0, keysLen = keys.length; j < keysLen; j++) {
        key = keys[j].replace(/^['"]/, '')
          .replace(/['"]$/, '');
        lastIter = j !== keys.length - 1;
        lastObj = obj;
        if ((key !== '' && key !== ' ') || j === 0) {
          if (obj[key] === undef) {
            obj[key] = {};
          }
          obj = obj[key];
        } else { // To insert new dimension
          ct = -1;
          for (p in obj) {
            if (obj.hasOwnProperty(p)) {
              if (+p > ct && p.match(/^\d+$/g)) {
                ct = +p;
              }
            }
          }
          key = ct + 1;
        }
      }
      lastObj[key] = value;
    }
  }
}
Sign up to request clarification or add additional context in comments.

Comments

0

Building on top of Mike Causer's answer I've made this function which takes into consideration multiple params with the same key (foo=bar&foo=baz) and also comma-separated parameters (foo=bar,baz,bin). It also lets you search for a certain query key.

function getQueryParams(queryKey) {
    var queryString = window.location.search;
    var query = {};
    var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
    for (var i = 0; i < pairs.length; i++) {
        var pair = pairs[i].split('=');
        var key = decodeURIComponent(pair[0]);
        var value = decodeURIComponent(pair[1] || '');
        // Se possui uma vírgula no valor, converter em um array
        value = (value.indexOf(',') === -1 ? value : value.split(','));

        // Se a key já existe, tratar ela como um array
        if (query[key]) {
            if (query[key].constructor === Array) {
                // Array.concat() faz merge se o valor inserido for um array
                query[key] = query[key].concat(value);
            } else {
                // Se não for um array, criar um array contendo o valor anterior e o novo valor
                query[key] = [query[key], value];
            }
        } else {
            query[key] = value;
        }
    }

    if (typeof queryKey === 'undefined') {
        return query;
    } else {
        return query[queryKey];
    }
}

Example input: foo.html?foo=bar&foo=baz&foo=bez,boz,buz&bar=1,2,3

Example output

{
    foo: ["bar","baz","bez","boz","buz"],
    bar: ["1","2","3"]
}

Comments

0

Here is a more-streamlined version of silicakes' approach.

The following function(s) can parse a querystring from either a USVString or Location.

/**
 * Returns a plain object representation of a URLSearchParams object.
 * @param {USVString} search - A URL querystring
 * @return {Object} a key-value pair object from a URL querystring
 */
const parseSearch = (search) =>
  [...new URLSearchParams(search).entries()]
    .reduce((acc, [key, val]) => ({
      ...acc,
      // eslint-disable-next-line no-nested-ternary
      [key]: Object.prototype.hasOwnProperty.call(acc, key)
        ? Array.isArray(acc[key])
          ? [...acc[key], val]
          : [acc[key], val]
        : val
    }), {});

/**
 * Returns a plain object representation of a URLSearchParams object.
 * @param {Location} location - Either a document or window location, or React useLocation()
 * @return {Object} a key-value pair object from a URL querystring
 */
const parseLocationSearch = (location) => parseSearch(location.search);

console.log(parseSearch('?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D'));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Here is a one-liner of the code above (125 bytes):

Where f is parseSearch

f=s=>[...new URLSearchParams(s).entries()].reduce((a,[k,v])=>({...a,[k]:a[k]?Array.isArray(a[k])?[...a[k],v]:[a[k],v]:v}),{})

Edit

Here is a method of serializing and updating:

const parseSearch = (search) =>
  [...new URLSearchParams(search).entries()]
    .reduce((acc, [key, val]) => ({
      ...acc,
      // eslint-disable-next-line no-nested-ternary
      [key]: Object.prototype.hasOwnProperty.call(acc, key)
        ? Array.isArray(acc[key])
          ? [...acc[key], val]
          : [acc[key], val]
        : val
    }), {});

const toQueryString = (params) =>
  `?${Object.entries(params)
    .flatMap(([key, values]) =>
      Array.isArray(values)
        ? values.map(value => [key, value])
        : [[key, values]])
    .map(pair => pair.map(val => encodeURIComponent(val)).join('='))
    .join('&')}`;

const updateQueryString = (search, update) =>
  (parsed =>
    toQueryString(update instanceof Function
      ? update(parsed)
      : { ...parsed, ...update }))
  (parseSearch(search));

const queryString = '?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D';
const parsedQuery = parseSearch(queryString);
console.log(parsedQuery);
console.log(toQueryString(parsedQuery) === queryString);

const updatedQuerySimple = updateQueryString(queryString, {
  foo: 'baz',
  x: 'z',
});
console.log(updatedQuerySimple);
console.log(parseSearch(updatedQuerySimple));

const updatedQuery = updateQueryString(updatedQuerySimple, parsed => ({
  ...parsed,
  ids: [
    ...parsed.ids,
    JSON.stringify([7,8,9])
  ]
}));
console.log(updatedQuery);
console.log(parseSearch(updatedQuery));
.as-console-wrapper { top: 0; max-height: 100% !important; }

Comments

-1

Many other solutions don't account for edge cases.

This one handles

  • null keys a=1&b=2&
  • null values a=1&b
  • empty values a=1&b=
  • unencoded equals signs a=1&b=2=3=4
  decodeQueryString: qs => {
    // expects qs to not have a ?
    // return if empty qs
    if (qs === '') return {};
    return qs.split('&').reduce((acc, pair) => {
      // skip no param at all a=1&b=2&
      if (pair.length === 0) return acc;
      const parts = pair.split('=');
      // fix params without value
      if (parts.length === 1) parts[1] = '';
      // for value handle multiple unencoded = signs
      const key = decodeURIComponent(parts[0]);
      const value = decodeURIComponent(parts.slice(1).join('='));
      acc[key] = value;
      return acc;
    }, {});
  },

Comments

1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.