ajbee
ajbee

Reputation: 3641

How to remap an Array of object to be Object of Object in Javascript

I have this value:

const sample = [{

  name: "name",
  value1: 1,
  mean: 2,
  create: 10,

  age: "age",
  player1: 20,
  player2: 40,

  sample: "sample",
  player3: 30,
  player4: 100,
}];

to be mapped into:

{
  name: {
    value: 1,
    mean: 2,
    create: 10
  },
  age: {
    player1: 20,
    player2: 40
  },
  sample: {
    player3: 30,
    player4: 100
  }
}

I know what I can do is to map it manually but my list is around 50 so it's not possible.

Edit:

The key property will be if it's string and the number will be the values in that order

Upvotes: 0

Views: 1804

Answers (7)

Soma Pál Papdi
Soma Pál Papdi

Reputation: 21

Your description is not clear, however, I think you want to remap a group of properties into a new property, only if key and value is the same.

Moreover, I put a new key = value = "other" and the properties afterwards are grouped under "other", so you don't have to specify all of the keys...

const sample = [{
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,

  age: "age",
  player1: 20,
  player2: 40,
}, {
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,

  age: "age",
  player1: 20,
  player2: 40,

  other: "other",
  p2: "ldkdskd",
  p1: 10
}];
    
function remapdata(data) {
  const obj = {};
  const keys = Object.keys(data);
  let lastGroup = undefined;
  let i = 0;`enter code here`
  while(i < keys.length) {
    if(keys[i] === data[keys[i]]) {
      obj[keys[i]]= {};
      lastGroup=keys[i];  
    } else if (lastGroup) {
      obj[lastGroup][keys[i]] = data[keys[i]];
    }
    i++;
  } 
  return obj;
}
const mapped = sample.map(remapdata);

console.log(mapped);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Upvotes: 2

Peter Seliger
Peter Seliger

Reputation: 13376

Everyone should be aware of that any approach which is going to restructure any object's key-value pair(s) is actually relying on a structure that always has to ensure, for e.g. Object.keys, a key order that exactly represents the order of any key's creation. Also the environment that creates such objects has to ensure exactly such a viable and reproducible order as well.

If one could generalize that, " ... whenever in a key-value pair key equals value, one wants to create a new object assigned to the resampled structure via key.", one should go with the beneath provided approach as is. Otherwise one has to limit the condition that is responsible for creating new sub-structures to any string type value which then also does limit the types the original structure is allowed to feature. ...

const sampleList = [{
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,

  age: "age",
  player1: 20,
  player2: 40,

  sample: "sample",
  player3: 30,
  player4: 100
}, {
  sample: "sample",
  player1: 130,
  player2: 1100,

  name: "name",
  value1: 11,
  mean: 12,
  create: 110,

  age: "age",
  player3: 120,
  player4: 140
}];


function restructureSample(sample) {
  const newSample = {};
  return Object.entries(sample).reduce((reference, tuple, idx, arr) => {

    const [key, value] = tuple;
  //if (typeof value === 'string') {  // - more generic, but limiting the use cases.
    if (key === value) {              // - maybe even more precise.

      reference = newSample[key] = {};
    } else {
      reference[key] = value;
    }
    return (((idx === (arr.length - 1)) && newSample) || reference);

  }, newSample);
}


console.log(sampleList.map(restructureSample));
.as-console-wrapper { min-height: 100%!important; top: 0; }

Upvotes: 1

Majed Badawi
Majed Badawi

Reputation: 28414

You can use .map function to iterate over the objects, and keep track of the last key to add integer-properties to them:

const sample = [
 {
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,
  age: "age",
  player1: 20,
  player2: 40,
  sample: "sample",
  player1a: 30,
  player2a: 100,
 },
 {
  name: "name",
  value1: 11,
  mean: 22,
  create: 130,
  age: "age",
  player1: 210,
  player2: 430,
  sample: "sample",
  player1a: 340,
  player2a: 1100,
 },
 {
  name: "name",
  value: 313,
  mean: 421,
  create: 23,
  age: "age",
  player1a: 440,
  player2a: 40,
 }
];
let lastKey = null;
let mapped = sample.map(elem => {
     let obj = { any: {} };
     let any = obj.any;
     for(var p in elem){
          let key = p, value = elem[p];
          if(typeof(value)=="string"){
               any[key] = {};
               lastKey = key;
          }else if(typeof(value)=="number"){
               if(lastKey)
                    any[lastKey][key] = value;
               else
                    any[key] = value;
          }
     }
     return obj;
});
console.log(mapped);

Note that you cannot have two same keys (player1 and player2) since it should be unique.

Upvotes: 0

Kunal Mukherjee
Kunal Mukherjee

Reputation: 5853

Use Array.prototype.reduce to reduce the array and remap the existing properties into your desired format.

const sample = [
 {
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,
  age: "age",
  player1: 20,
  player2: 40,
 }
];

const result = sample.reduce((acc, { value1, mean, create, player1, player2 }) => {
    acc.push({
        name: {
            value1,
            mean,
            create
        },
        age: {
            player1,
            player2
        }
    });
    return acc;
}, []);

console.log(result);

For dynamic keys you can do something like -

const sample = [
 {
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,
  age: "age",
  player1: 20,
  player2: 40,
 }
];

const keyMapping = {
    name: ['value1', 'mean', 'create'],
    age: ['player1', 'player2']
};

const result = sample.reduce((acc, curr) => {
    const obj = Object.entries(keyMapping).reduce((accumulator, [key, val]) => {
        accumulator[key] = val.reduce((r, c) => {
            r[c] = curr[c];
            return r;
        }, Object.create(null));
        return accumulator;
    }, Object.create(null));
    
    acc.push(obj);
    return acc;
}, []);

console.log(result);

Upvotes: 0

Sven.hig
Sven.hig

Reputation: 4519

You probably want something like this

const sample = [ { name: "name", value1: 1, mean: 2, create: 10, age: "age", player1: 20, player2: 40, }, ];

res = sample.reduce((r, c) => {
  let key;
  Object.entries(c).forEach((o) => {
(k = o[0]), (v = o[1]);
typeof v == "string" ? ((key = v), (r[v] = {})) : (r[key][k] = v);
  });
  return r;
}, {});
console.log(res);

Upvotes: 1

goto
goto

Reputation: 4425

To do this the right way, you need to specify properties that will fall under name and age in the final object, something like in the following example:

const sample = [
  {
    name: "name",
    value1: 1,
    mean: 2,
    create: 10,
    age: "age",
    player1: 20,
    player2: 40
  },
  {
    name: "fred",
    value1: 3,
    mean: 5,
    create: 101,
    age: "age",
    player1: 202,
    player2: 401
  }
];


const keyValuesMap = {
  // `name` property will have `value1`, `mean`, and `create` properties
  name: ["value1", "mean", "create"],
  
  // `age` property will have `player1` and `player2` properties
  age: ["player1", "player2"]
};

const result = sample.map((item) => {
  // map over the `sample` data and get the key/value pairs of each
  // item, then use `reduce` to create a new object based on the
  // `keyValuesMap` specified above
  return Object.entries(item).reduce((accumulator, [key, value]) => {
    const name = accumulator.name || {};
    const age = accumulator.age || {};

    // if value belongs to the `name` property, add to it
    if (keyValuesMap.name.includes(key)) {
      name[key] = value;
    }
    // if value belongs to the `age` property, add to it
    if (keyValuesMap.age.includes(key)) {
      age[key] = value;
    }

    accumulator.name = name;
    accumulator.age = age;

    return accumulator;
  }, {});
});

console.log("result", result);

References:

Upvotes: 1

Sascha A.
Sascha A.

Reputation: 4616

Use Array#map for mapping it to the new object-structure inside.

const sample = [
 {
  name: "name",
  value1: 1,
  mean: 2,
  create: 10,
  age: "age",
  player1: 20,
  player2: 40,
 }
];

let result = sample.map(elem => ({
    any : {
        name: {
           value: elem.value1,
           mean: elem.mean,
           create: elem.create
        },
        age: {
           player1: elem.player1,
           player2: elem.player2
        }
   }
}))

console.log(result);

Upvotes: 1

Related Questions