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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.