8

I have an array of objects. Each object has a lot of keys (more than 100) and some of these keys can have special chars that I would like to remove.

I try to do what I want in this way:

const result = data.map(datum => {
  const keys = Object.keys(datum)
  const replacedKeys = keys.map(key => {
    const newKey = key.replace(/[.|&;$%@%"<>+]/g, '')
  })
  // ??
})

But I'm sure it's not the right way..

6 Answers 6

4

You could map new object with a new key and create a single object with Object.assign.

const result = data.map(datum => Object.assign(...Object
    .keys(datum)
    .map(key => ({ [key.replace(/[.|&;$%@%"<>+]/g, '')]: datum[key] }))
));
Sign up to request clarification or add additional context in comments.

1 Comment

Just so others understand what is being given as the input: ` data = [ { someKey: 1 }, { some0Key: 1 }, { some1Key: 1, some2Key: 1, }, { some3Key: 1, some4Key: 1, some5Key: 1, some6Key: 1, some7Key: 1, some8Key: 1, some9Key: 1, some10Key: 1, }, { some11Key: 1, some12Key: 1, some13Key: 1, some14Key: 1, some15Key: 1, }, ]; `
3

With the ES8 Object.fromEntries method that has already found its way in FireFox, you can do:

const sanitiseKeys = o => Object.fromEntries(Object.entries(o).map(([k,v]) => 
                                            [k.replace(/[.|&;$%@%"<>+]/g,""), v]));

// Example use:
var data = [{ "name#": "John" }, { "@key": 2 }];

data = data.map(sanitiseKeys);

console.log(data);

If not yet implemented, here is a polyfill:

Object.fromEntries = arr => Object.assign({}, ...arr.map( ([k, v]) => ({[k]: v}) ));

8 Comments

Wouldn't your polyfill cause problems with large objects with lots of keys? Normally there's a limit of how many arguments a function can take.
@DoMiNeLa10 "Normally there's a limit of how many arguments a function can take"?
@guest271314 that's the specification which has nothing to do with how implementations treat it. Most of them have limits, with jscore being especially low. If we were going by the spec, I could say ECMAScript has TCO, but almost no implementations actually have it.
|
1

This solution relies on String.prototype.replace(), so it can take Strings or RegExps as the source, and allows for subsitution. Keep in mind that it's not very performant, but it uses only pure functions:

const data = {
  someKey:   1,
  some0Key:  1,
  some1Key:  1,
  some2Key:  1,
  some3Key:  1,
  some4Key:  1,
  some5Key:  1,
  some6Key:  1,
  some7Key:  1,
  some8Key:  1,
  some9Key:  1,
  some10Key: 1,
  some11Key: 1,
  some12Key: 1,
  some13Key: 1,
  some14Key: 1,
  some15Key: 1,
};

// simple equivalent of proposed Object.fromEntries()
const fromEntries = (entries) =>
      entries.reduce((obj, [key, value]) => ({
        [key]: value,
        ...obj
      }), {});

const replaceObjectKeys = (obj, from, to) =>
      fromEntries(
        Object.entries(obj)
          .map(([key, value]) => [key.replace(from, to), value]));

console.log(replaceObjectKeys(data, /Key$/, 'prop'));

fromEntries could be easily rewritten into a faster implementation at the cost of introducing mutable variables.

Comments

1

You can convert the plain JavaScript object to JSON using JSON.stringify() and match the property of valid JSON using String.prototype.replace(), then convert back to a plain JavaScript object using JSON.parse().

Removed " from character class as valid JSON property is surrounded by double quotes ".

The RegExp

([.|&;$%@%<>+]+)(?=([^\1]+|)":)

creates a capture group containing the character class and matches the character class followed by one or more characters not in the character class followed by closing double quote of the property name " followed by colon character or double quotes followed by colon character.

The matched character class can be replaced with an empty string '' or any other character.

let o = {"a.B|c&D;0$_%@q%<Z>5+":1};

console.log(o);

o = JSON.parse(JSON.stringify(o).replace(/([.|&;$%@%<>+]+)(?=([^\1]+|)":)/g, ''));

console.log(
  JSON.stringify(o)
, /[.|&;$%@%<>+]+/.test(Object.keys(o)[0]) // false
);

Comments

0

Consider using Array#reduce() to aggregate values for keys of your input object. The reason for this is that your key sanitization (ie removing unwanted characters from keys) may cause distinct key/value pairs of your input object to "reduce", so that a sanitized key effectively relates to multiple values. For instance an input object like:

const data = {
  'key' : 'value0',
  'key&&&' : 'value1',
  'key$%<>' : 'value2'
}

would (based on your sanitation) yield an output object with a single key relating to multiple values:

const data = {
  'key' : 'value0', // what about value1, value2 ?
}

To address this, you might consider aggregating values with common sanitized keys to an array as shown:

const data = {
  'key' : 'value0',
  'key&&&' : 'value1',
  'key$%<>' : 'value2',
  'foo' : 'value3'
}

const result = Object.entries(data).reduce((obj, [ key, value ]) => {
  
  const sanitizedKey = key.replace(/[.|&;$%@%"<>+]/g, '');
  const objValue = obj[ sanitizedKey ]
  
  /* 
  Account for conflicting keys after santizing by grouping
  values in a nested array
  */
  if(objValue) {
    obj[ sanitizedKey ] = [value].concat(objValue)
  }
  else {
    obj[ sanitizedKey ] = value
  }
  
  return obj;
  
}, {});

console.log(result)

Comments

0

To extend @Nina's answer, here is the full representation:

data = [
  {someKey:   1},
  {some0Key:  1},
  {some1Key:  1,
    some2Key:  1},
  {some3Key:  1,
    some4Key:  1,
    some5Key:  1,
    some6Key:  1,
    some7Key:  1,
    some8Key:  1,
    some9Key:  1,
    some10Key: 1,
  },
  {some11Key: 1,
    some12Key: 1,
    some13Key: 1,
    some14Key: 1,
    some15Key: 1,}
];
result = data.map(datum => Object.assign(...Object
    .keys(datum)
    .map(key => ({ [key.replace(/some/g, 'bum')]: datum[key] }))
));

results:

result === [
  {bumKey:   1},
  {bum0Key:  1},
  {bum1Key:  1,
    bum2Key:  1},
  {bum3Key:  1,
    bum4Key:  1,
    bum5Key:  1,
    bum6Key:  1,
    bum7Key:  1,
    bum8Key:  1,
    bum9Key:  1,
    bum10Key: 1,
  },
  {bum11Key: 1,
    bum12Key: 1,
    bum13Key: 1,
    bum14Key: 1,
    bum15Key: 1,}
];

Comments

Your Answer

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