1

I have the following url with params:

abc?carbs=pasta,rice,noodles&sauce=tomato&wine=red

As you can see, carbs is an array divided by a comma. So, by using the following code:

sourceURL
    .slice(1)
    .split('&')
    .map(p => p.split('='))
    .reduce((obj, pair) => {
      const [key, value] = pair.map(decodeURIComponent);
      return { ...obj, [key]: value };
    }, {});

I get this NOT CORRECT result:

{
    "carbs": "pasta,rice,noodles",
    "sauce": "tomato",
    "wine": "red"
}

What I would like is the following: (EXPECTED)

{
    "carbs": ["pasta","rice","noodles"],
    "sauce": "tomato",
    "wine": "red"
}

Any way someone can help? Thanks in advance, Joe

UPDATE: Some of the responses are great, thanks, everyone. Unfortunately, they all return carbs as an Object if it contains only 1 value.

For example, if my URL is abc?carbs=noodles&sauce=tomato&wine=red I should get:

{
    "carbs": ["noodles"], <----- still an array even with 1 
    "sauce": "tomato",
    "wine": "red"
}

but unfortunately, all the provided solutions return the following:

{
    "carbs": "noodles", 
    "sauce": "tomato",
    "wine": "red"
}

Sorry if it wasn't clear before. Thanks Joe

4 Answers 4

4

Use URLSearchParams to parse the string and .reduce to get the desired result :

EDIT

Carbs needs always to be an array, even if it's empty

add a check for the key inside .reduce :

const sourceURL = "abc?carbs=&sauce=tomato&wine=red";

const parsed = new URLSearchParams(sourceURL.split("?")[1]);

const result = [...parsed.entries()].reduce((acc, [key, value]) => {
  if (key === "carbs") acc[key] = value.split(",").filter(e => e);
  
  else acc[key] = value.includes(",") ? value.split(",") : value;
  
  return acc;
}, {});

console.log(result);

Sign up to request clarification or add additional context in comments.

4 Comments

If we write the params string as carbs=pasta&carbs=rice&carbs=noodles&sauce=tomato&wine=red, then new URLSearchParams('carbs=pasta&carbs=rice&carbs=noodles&sauce=tomato&wine=red').getAll( 'carbs' ); will automatically return the array ['pasta',noodles','tomato']. So I would also suggest the OP to think about if changing the API is an option, since then URLSearchParams does everything the OP needs out of the box.
@taki unfortunately in your code, if the carbs param returns only 1 key, it becomes an object. Carbs needs always to be an array, even if it's empty. Any suggestion?
@Joe can you add an example ( in fiddle or somewhere ) ? because i don't see an Object when it's only one param, and i edited the answer to add a check for carbs
check in your code, maybe you or someone else is overwriting the String.prototype.split (it returns an array not an object) or another prototype function
1

Maybe something like this?

const sourceURL = "abc?carbs=pasta,rice,noodles&sauce=tomato&wine=red"

const res = sourceURL
    .split("?")
    .pop()
    .split('&')
    .map(p => p.split('='))
    .reduce((obj, pair) => {
      const [key, value] = pair.map(decodeURIComponent);
      return { ...obj, [key]: value.indexOf(",") >= 0 ? value.split(",") : value  };
    }, {});

console.log(res)

OUTPUT

{ carbs: [ 'pasta', 'rice', 'noodles' ],
  sauce: 'tomato',
  wine: 'red' }

1 Comment

Some explanations of the steps or comments in code could help to understand your fine solution.
1

You can use a RegExp to get the pairs, and then split the pairs by equal and comma signs, normalise the subarrays to have an entry structure [key, value], and then convert to and Object with Object.fromEntries():

const parse = url => Object.fromEntries( // convert the entries to an object
  url.match(/[^?=&]+=[^?=&]+/g) // match all pairs with = in the middle
    .map(s => s.split(/[=,]/)) // split the pairs by = and ,
    .map(arr => arr.length > 2 ? [arr[0], arr.slice(1)] : arr) // normalise the entries
)

console.log(parse('abc?carbs=pasta,rice,noodles&sauce=tomato&wine=red'))

console.log(parse('abc?carbs=pasta&sauce=tomato&wine=red'))

1 Comment

Sorry @ori, there are cases where the param url could be like this: 'abc?carbs=pasta&sauce=tomato&wine=red' ... which means only 1 item needs to be converted into array inside carbs. Your code expect to have over 2 arrays. That's not working. Any update? I could also get this 'abc?sauce=tomato&wine=red' where carbs is not in the param but I still pass carbs: []
0

Could you not check for commas and if found do a split ?

    .slice(1)
    .split('&')
    .map(p => p.split('='))
    .reduce((obj, pair) => {
      let [key, value] = pair.map(decodeURIComponent);
      value = value.indexOf(',') !== -1 ? value.split(',') : value;
      return { ...obj, [key]: value };
    }, {});

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.