1
votes

I have a javascript (node.js) object:

const fileObj = {filename: 'file1', filepath: '/file1/path/', flag: true,}

With objects, I was told there is no guarantee of order (unlike arrays which are explicitly ordered) -- So can I be certain that

Object.values(fileObj)

will spit out the values in the same order as the keys spit out with

Object.keys(fileObj)

The reason this is critical is using mysql escaped values in node:

const mysql = require('mysql');
const connection = mysql.createConnection(credentials); 

const fileObj = {filename: 'file1', filepath: '/file1/path/', flag: true,}
const cols = Object.keys(fileObj).join(', ');
const placeholder = Object.keys(fileObj).fill('?').join(', ');

const sql = `INSERT INTO table1 (${cols}) VALUES (${placeholder})`;

connection.query(sql, Object.values(fileObj), function (error, results, fields) {
    // Do stuff
});

This creates a SQL statement:

sql = INSERT INTO table1 (filename, filepath, flag) VALUES (?, ?, ?);

Which theoretically is then coupled with the escape values:

connection.query(sql, ['file1', '/file1/path/', true], function...

But if they are not all in the same order, game over...


Related questions:

Does JavaScript Guarantee Object Property Order? (Javscript does not guarantee order... except maybe you can expect order...)

Does ES6 introduce a well-defined order of enumeration for object properties? (Order guaranteed sometimes... but not with Object.keys?)

Which is it? And what should I be using to make sure my mySQL escape values are in the same order as my mySQL column names?

4
Use Object.entries() - SLaks
Use Object.entries and map over it twice! - Ry-
Or use Object.entries per other suggestions and map over it once using the INSERT... SET... SQL syntax. - fubar
Object.entries(yourObject) does not guarantee the order of the entries, but it does keep the keys:values paired. - Stephen P
According to a few of the people involved in writing ES specs that language does not guarantee order since in the end all the if x was invoked as boil down to an internal method that does not specify order at all - slebetman

4 Answers

3
votes

I would use Object.entries per the suggest of multiple other users.

You can then choose to continue to extract the keys and values, this time with the order guaranteed for use in your SQL statement.

var attributes = {
  a: 'A',
  f: 'F',
  c: 'C',
  b: 'B',
  e: 'E',
  d: 'D',
};

const entries = Object.entries(attributes);
const keys = entries.map(entry => entry[0]);
const values = entries.map(entry => entry[1]);

console.log(keys, values);
2
votes

You might convert your object into arrays in a single loop. It will guarantee the correct order:

const fileObj = {filename: 'file1', filepath: '/file1/path/', flag: true,}

const vals = [], placeholder = [];
const keys = Object.keys(fileObj).map(k => vals.push(fileObj[k]) && placeholder.push('?') && k);

console.log(`INSERT INTO table1 (${keys}) VALUES (${placeholder})`);
console.log(keys + ''); console.log(vals + '');
1
votes

If you use the mysql2 npm package, you can use this much better syntax.

const fileObj = {filename: 'file1', filepath: '/file1/path/', flag: true,}
const sql = `INSERT INTO table1 SET ?`;
const result = await connection.query(sql, [fileObj]);
0
votes

I'd personally not use any hackish manipulation of objects (the design of which originally is supposed to be unordered map of key to values) because they can be implementation defined (and sometimes vary between versions of the same interpreter).

If you want order use an array:

const fileObj = [
  {
    key: 'filename',
    val: 'file1'
  },
  {
    key: 'filepath',
    val: '/file1/path/'
  },
  {
    key: 'flag',
    val: true,
  }
];

This is unambiguous and works on all versions and implementations of JS.