-3

I have this hash:

{
 a: [1,2],
 b: [1,2,3]
}

I need to generate a string like this:

a=1&a=2&b=1&b=2&b=3

How can I solve this problem ? I'm taking a look at lodash but I cant solve it.

Thanks.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
  • 3
    What have you tried? Why are you looking at lodash? Why not use a simple loop? – Bergi Mar 30 '16 at 14:19
  • 1
    Maybe habe a look at [Convert javascript object to URL parameters](http://stackoverflow.com/q/22678346/1048572) for some ideas – Bergi Mar 30 '16 at 14:23
  • I would still hold off on ES6 solutions unless you **know** that enough of your users support the features you want. I'd stick to one of the simple loops that were proposed (including my answer) – xyhhx Mar 30 '16 at 14:31
  • 2
    **This is not a duplicate of the question shown**! A string like `a=1&a=2` would be *overwriting* the previous `a` variable. This is NOT what the original poster is asking for. – xyhhx Mar 30 '16 at 14:42
  • 1
    I agree with @Martin: Not the same question, and does not provide a suitable solution. Thus I have flagged for reopen... there *might* be a duplicate out there, but the proposed one is not it – musefan Mar 30 '16 at 14:50
  • If you're using jQuery [`$.param({ a: [1,2], b: [1,2,3] }, true)`](https://api.jquery.com/jQuery.param/#jQuery-param-obj-traditional) results in the desired string. – 3limin4t0r Jun 26 '23 at 19:30

9 Answers9

5

Using javascript Object.keys(), map() and join() with ES6 arrow function

var arr = {
  a: [1, 2],
  b: [1, 2, 3]
};
var res = Object.keys(arr).map(v => arr[v].map(v1 => v + '=' + v1).join('&')).join('&');
document.write(res);

Or without arrow function

var arr = {
  a: [1, 2],
  b: [1, 2, 3]
};

var res = 
    // get object keys
    Object.keys(arr)
// iterate over object keys and iterate
.map(function(v) {
  // iterate over inner array
  return arr[v].map(function(v1) {
    // generate prefered string and return 
    return v + '=' + v1
    // cconcatenate string array
  }).join('&')
  // concatenate string array
}).join('&');

document.write(res);
Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188
  • Or, just for fun, instead of two `joins`, if the input doesn't contain any comas: `Object.keys(arr).map(v => arr[v].map(v1 => v + '=' + v1)).join().replace(/,/g, '&')` :) – XCS Mar 30 '16 at 15:00
  • The inner `.join('&')` can removed if you replace the outer `.map(...)` with `.flatMap(...)` – 3limin4t0r Jun 27 '23 at 08:57
4

For a nice ES5 solution:

function hashToString(hash) {

    var str = '';
    for ( prop in hash ) {
        if ( !hash.hasOwnProperty(prop) ) continue;
        for (var i = 0; i < hash[prop].length; i++) {
            str += prop + '=' + hash[prop][i] + '&';
        };
    }
    str = str.slice(0, str.length-1);
    return str;

}

Try that out!

It works:

enter image description here


Okay! So what's going on?

First, you want to loop through each item on the first level.

You do this with a for ... in loop. You should be checking hasOwnProperty so you don't go through unexpected prototype properties.

Then, within that first loop, you want to loop through the items in the current item:

We'll do that with a regular for loop because these items are just arrays.

In here, we want to adjust our string:

str += prop + '=' + hash[prop][i] + '&';

This adds our property name (saved as prop from the for ... in loop), then the value in that property's array at the given index i. We'll leave it with a trailing & that we will remove just outside of the loop.

xyhhx
  • 6,384
  • 6
  • 41
  • 64
  • I think you slightly exaggerated your answer, but it's 100% correct and useful. Grab a +1 :). – Fka Apr 07 '16 at 06:35
  • 1
    Yeah, it is a bit long winded. But when I started off, I learned *a lot* from long answers like this. They can show you not just the correct code, but the way to *think* when coding. I like to show what my train of thought is when I put it together, because often time that mode of thinking will help in future problems :) – xyhhx Apr 07 '16 at 16:23
1

Use a for in loop to iterate over each property in your hash object, and from there, add each value to the string, like so:

function hashToQueryString(hash) {
    var query = '';
    for (key in hash) {
        for (i=0; i<=key.length; i++) {
            query += key+'='+ myJSON[key]+'&';
        }
    }
    //remove the trailing &
    query = query.substr(0, query.length -1);
    return query;
}

(Keep in mind that you should also be checking that each property in hash contains a valid array, and that you're not accidentally enumerating any properties you don't want)

dcastrodale
  • 87
  • 1
  • 4
0

You could use qs for that. It will also handle all the encoding needed if you happen to go beyond integers.

qs.stringify({
    a: [1,2],
    b: [1,2,3]
}, { indices: false });
Li0liQ
  • 11,158
  • 35
  • 52
  • 1
    this is why there are sooo many libraries, such a simple task with a couple lines of code, why a new library to do it :( – musefan Mar 30 '16 at 14:24
  • @musefan Those libraries free up your brain power so you could spend it on really creative tasks instead of ones that have already been properly solved by others. – Li0liQ Mar 30 '16 at 14:26
  • @Li0liQ That's not a good reason to bring in a library for one task. – xyhhx Mar 30 '16 at 14:30
  • 1
    If you need the rest of the library, maybe... if you need this one tiny thing, then it's quicker to write own code than to find, install and learn a library – musefan Mar 30 '16 at 14:32
  • @Martin Fair enough. However, have you noticed that among the 7 answers suggested only two took into account url encoding? Isn't it a good enough reason to rely on a thoroughly tested library? And if the code volume is of concern, just use [rollup](https://github.com/rollup/rollup). – Li0liQ Mar 30 '16 at 19:41
  • Well the poster is not asking about url encoding. So no, I definitely would not recommend using a library for query string or url encoding. – xyhhx Mar 30 '16 at 19:45
  • @xyhhx If you see `key=value&foo=bar` you know it's probably either URL encoding or x-www-form-encoded. If you provide a solution without it, the next question of OP will be "How do I convert @ to %40 and $ into %24?" So accounting for URL encoding or at least mentioning that special characters must be encoding doesn't hurt. – 3limin4t0r Jun 26 '23 at 20:42
  • @xyhhx Also, URL encoding is more complex than you might think. A lot of modern apps allow you to send structured objects via query parameters. `{a:{b:1,c:[2,3],d:{e:4}},f:5 }` is often encoded as `a[b]=1&a[c][]=2&a[c][]=3&a[d][e]=4&f=5`—where keys, values, `[` and `]` are URL encoded. – 3limin4t0r Jun 26 '23 at 20:48
0

If you have to stay with poor old ES5, and don't want to use another library for a simple task:

var myHash = {
   a: [1,2],
   b: [1,2,3]
}
var myString = '';
Object.keys(myHash).map(function(key, index) {
  myHash[key].map(function(value) {
    myString += (myString ? '&' : '') + key + '=' + value;
  });
});

jsfiddle

MarcoS
  • 17,323
  • 24
  • 96
  • 174
0

I am sure there is a cleaner way, but here is some options with reduce and map.

var obj = {
 a: [1,2],
 b: [1,2,3]
};

var params = Object.keys(obj).reduce( function (arr, key){
    arr.push(key + "=" + obj[key].join("&" + key + "="))
    return arr; 
}, []).join("&");

or if the values need to be encoded for the querystring

var params = Object.keys(obj).reduce( function (arr, key){
    var mapped = obj[key].map( function (val) { return key + "=" + encodeURIComponent(val); }).join("&");
    arr.push(mapped)
    return arr; 
}, []).join("&");
epascarello
  • 204,599
  • 20
  • 195
  • 236
0

let hash = {
  a: [1, 2],
  b: [1, 2, 3]
};
let str = '';
for (let key in hash) {
  for (let value of hash[key]) {
    str += `${key}=${value}&`;
  }
}
alert(str.slice(0, -1));
Lewis
  • 14,132
  • 12
  • 66
  • 87
0

Here is a simple javascript hash one-liner I made named pr2:

function pr2(str){let e=9833;for(let n=0;n<str.length;n++){const r=str.charCodeAt(n);e=43*e^r}return e>>>0}

Though if that is not good enough for your purpose, here is MurMurHash 3:

function murmurhash3_32(str, seed) {
    // MurMurHash is better than djb2, djb2 XOR, and FNV hash in speed, security, and collision probability.
    // Version 3 is better than version 2
    // Javascript implementation of MurMurHash 3
    // String must be ASCII only!
    // Seed must be positive integer only!
    var strLength = str.length;
    var remainder = strLength & 3; // strLength % 4
    var bytes = strLength - remainder;
    var h1 = seed;
    var c1 = 0xcc9e2d51;
    var c2 = 0x1b873593;
    var i = 0;

    while (i < bytes) {
      var k1 =
        (str.charCodeAt(i) & 0xff) |
        ((str.charCodeAt(++i) & 0xff) << 8) |
        ((str.charCodeAt(++i) & 0xff) << 16) |
        ((str.charCodeAt(++i) & 0xff) << 24);
      ++i;

      k1 =
        ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
        0xffffffff;
      k1 = (k1 << 15) | (k1 >>> 17);
      k1 =
        ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
        0xffffffff;

      h1 ^= k1;
      h1 = (h1 << 13) | (h1 >>> 19);
      var h1b =
        ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
      h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
    }

    var k1 = 0;

    switch (remainder) {
      case 3:
        k1 ^= (str.charCodeAt(i + 2) & 0xff) << 16;
      case 2:
        k1 ^= (str.charCodeAt(i + 1) & 0xff) << 8;
      case 1:
        k1 ^= str.charCodeAt(i) & 0xff;

        k1 =
          ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
          0xffffffff;
        k1 = (k1 << 15) | (k1 >>> 17);
        k1 =
          ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
          0xffffffff;
        h1 ^= k1;
    }

    h1 ^= strLength;

    h1 ^= h1 >>> 16;
    h1 =
      ((h1 & 0xffff) * 0x85ebca6b +
        ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) &
      0xffffffff;
    h1 ^= h1 >>> 13;
    h1 =
      ((h1 & 0xffff) * 0xc2b2ae35 +
        ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) &
      0xffffffff;
    h1 ^= h1 >>> 16;

    return h1 >>> 0;
  }
// end of function


  let string = prompt('string to hash')
let seed = prompt('seed for the hash (positive integer)')
let hashed = murmurhash3_32(string, seed)
console.log('Hashed: ' + hashed)
alert('Hashed: ' + hashed)
  • 1
    You must not have read the question in full. The input is `{ a: [1,2], b: [1,2,3] }` which should output `"a=1&a=2&b=1&b=2&b=3"`. So OP used "hash", probably because they didn't know any better. The intent is to encode the input, probably in URL query parameter format, but could also be x-www-form-urlencoded. As it stands, this answer is not relevant to the question. – 3limin4t0r Jun 26 '23 at 20:32
0

If the intent is to convert data to an URL query parameters, note that special URL characters should be URL encoded. JavaScript provides URLSearchParams to build URL query/search parameters, without worrying about encoding special characters.

const input = {
  a: [1, 2],
  b: [1, 2, 3],
};

const searchParams = new URLSearchParams();
for (const key in input) {
  input[key].forEach(value => searchParams.append(key, value));
}
const desired = searchParams.toString();

console.log(desired);

Note that I use for...in iteration which iterates all iterable properties on the prototype chain. You could replace this easily if needed, for example with:

for (const [key, values] of Object.entries(input)) {
  values.forEach(value => searchParams.append(key, value));
}

Which iterates only the own properties of an object.

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52