简体   繁体   中英

Transform an array of key/value pairs into array of objects

I need to be able to convert an array into a new array containing multiple objects. For example, if I have this array:

["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"]

I want to be able to convert it into this:

[{
   "name": "Tom",
   "id": "48688"
}, {
   "name": "Bob"
   "id": "91282"
}]

Use a for loop that increments its iteration by 4 , like so:

let results = [];
for(let i = 0; i < array.length; i += 4) {    // increment i by 4 to get to the start of the next object data
  results.push({
    id: array[i + 3],                         // array[i + 0] is the string "name", array[i + 1] is the name,
    name: array[i + 1]                        // array[i + 2] is the string "id" and array[i + 3] is the id
  });
}

Demo:

 let array = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282", "name", "Ibrahim", "id", "7"]; let results = []; for(let i = 0; i < array.length; i += 4) { results.push({ id: array[i + 3], name: array[i + 1] }); } console.log(results);

It is common to see a zip function taking a key k and a value v and create an object with them:

const zip =
  (k, v) =>
    ({[k]: v});

zip("name", "Tom");
//=> {name: "Tom"}

If both key and value are in an array you can spread it in a zip call like that zip(...arr) . Or you can modify the signature a little bit:

const zip =
  ([k, v]) =>
    ({[k]: v});

zip(["name", "Tom"]);
//=> {name: "Tom"}

If the array contains multiple pairs of keys and values then we can design a recursive version of zip :

const Nil = Symbol();

const zip =
  ([k = Nil, v = Nil, ...xs], o = {}) =>
    k === Nil && v === Nil
      ? o
      : zip(xs, (o[k] = v, o));
      
zip(["name", "Tom", "id", "48688"]);
//=> {name: "Tom", id: "48688"}

We can now think about slicing your array into chunks of equal number of pairs and apply zip to each chunk.

First let's write a slices function that will cut an array into slices of n elements:

const slices =
  (xs, n, ys = []) =>
    xs.length === 0
      ? ys
      : slices(xs.slice(n), n, (ys.push(xs.slice(0, n)), ys));

slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4);
//=> [["name", "Tom", "id", "48688"],["name", "Bob", "id", "91282"]]

We can now apply zip to each chunk:

slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4)
  .map(chunk => zip(chunk));
//=> [{name: "Tom", id: "48688"},{name: "Bob", id: "91282"}]

 const Nil = Symbol(); const zip = ([k = Nil, v = Nil, ...xs], o = {}) => k === Nil && v === Nil ? o : zip(xs, (o[k] = v, o)); const slices = (xs, n, ys = []) => xs.length === 0 ? ys : slices(xs.slice(n), n, (ys.push(xs.slice(0, n)), ys)); console.log( slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4) .map(chunk => zip(chunk)) );

I see questions like this very, very often, so I made a little converter that accomplishes this particular goal:

 // input var inputArray = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"] var sizeOfObjects = 2; // amount of entries per object // function function convert(array, size) { var newArray = [] //set up an array var res3 = array.reduce((acc, item, index) => { if (index % 2 == 0) { // if the index is even: acc[`${item}`] = array[index+1]; // add entry to array } if (Object.keys(acc).length == size) { // if the desired size has been reached: newArray.push(acc); // push the object into the array acc = {}; // reset the object } return acc; // preserve accumulator so it doesn't get forgotten }, {}); // initial value of reducer is an empty object return newArray; //return the array } console.log(convert(inputArray, sizeOfObjects));

Hopefully this helps people who are looking for an answer for this kind of question.

If you're looking to just create a single object, look at this other question/answer: Create object from array

You could take a dynamic approach by using an object for keeping track of the target index for same named keys.

 const getArray = data => { let indices = {}, result = [], i = 0; while (i < data.length) { const [key, value] = data.slice(i, i += 2); indices[key] ??= 0; (result[indices[key]++] ??= {})[key] = value; } return result; }, data1 = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], data2 = ["name", "Tom", "id", "48688", "color", "green", "name", "Bob", "id", "91282", "color", "red"]; console.log(getArray(data1)); console.log(getArray(data2));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

We can use % operator to decide whether we find an object to insert into array or not:

 const data = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"]; makeObjectArray = arr => { const result = [], temp = []; arr.forEach((a, i)=>{ if (i % 2 == 0) temp.push({ [arr[i]]: arr[i + 1]}) if (i % 3 == 0 && i != 0) { result.push(Object.assign({}, ...temp)); temp.length = 0; } }) return result; } console.log(makeObjectArray(data))

you can break the array into smaller chunks of the desired size with a helper function like:

function chunk(to_chunk, chunk_size) {
    var output = [];
    if(to_chunk.length > chunk_size) {
        output.push(to_chunk.slice(0, chunk_size));
        output.push(chunk(to_chunk.slice(chunk_size)));
        return output;
    } else {
        return to_chunk;
    }
}

Then you can map the result with other function to return your desired object:

var final = chunk(seed, 4).map((x) => myObject(x));
function myObject(seed) {
    var output = {};
    output[seed[0]] = seed[1];
    output[seed[2]] = seed[3];
    return output;
}

I think this approach is nice in terms of readability, putting all together you have:

 var seed = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"]; var final = chunk(seed, 4).map((x) => myObject(x)); console.log(final); function chunk(to_chunk, chunk_size) { var output = []; if(to_chunk.length > chunk_size) { output.push(to_chunk.slice(0, chunk_size)); output.push(chunk(to_chunk.slice(chunk_size))); return output; } else { return to_chunk; } } function myObject(seed) { var output = {}; output[seed[0]] = seed[1]; output[seed[2]] = seed[3]; return output; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM