2

I have a large object, and not each element within the object has the same keys or number of keys. The first element has all the keys, and this first element's values are what I want to actually USE as the keys for each element, filling in with blanks where the element does not have this key.

I can wrap my brain around WHAT needs to happen, but I just can't figure out how to code it. This is the sample as I import it:

[
    {
        "A": "Name",
        "B": "Type",
        "C": "Company",
        "D": "Year",
        "E": "Alt1",
        "F": "Name1",
        "G": "Alt2",
        "H": "Name2",
        "I": "Notes"
    },
    {
        "A": "Skittles",
        "C": "Mars",
        "D": 0,
        "E": "Cadbury",
        "F": "Sour Patch Kids",
        "I": "CLEAN"
    },
    {
        "A": "Love",
        "B": "Chocolate",
        "C": "Dove",
        "D": 0,
        "E": "0",
        "F": "0",
    }
]

In this example, the second element is missing keys "B", "G", and "H", while the third element is missing keys "G", "H" and "I". So using the FIRST element's keys A through I as a master template, I want to rewrite a new object, that looks like this:

[
    {
        "Name": "Skittles",
        "Type": "",
        "Company": "Mars",
        "Year": 0,
        "Alt1": "Cadbury",
        "Name1": "Sour Patch Kids",
        "Alt2": "",
        "Name2": "",
        "Notes": "CLEAN"
    },
    {
        "Name": "Love",
        "Type" : "Chocolate",
        "Company": "Dove",
        "Year": 0,
        "Alt1": "",
        "Name1": "",
        "Alt2": "",
        "Name2": "",
        "Notes": ""
    }
]

I've written several for loops but I cannot seem to grasp the complexity of this... This is as far as I've gotten:

a = [];
b = [];
new_keys = [];

/* Capture the new keys */
for(var v in user_file[0]) {
  if(user_file[0].hasOwnProperty(v)) {
    new_keys.push(user_file[0][v]);
  }
}

/* user_file is the object I pasted above... */
for ( var i = 0 ; i<user_file.length; i++ ) {
    /* write the b object */

    /* then push that object into a */
    a.push(b);

    /* Empty out b */
    b = [];
}

I have no idea how to write this b[] object....

Brian Powell
  • 3,336
  • 4
  • 34
  • 60

5 Answers5

2

You're really close.

In Javascript, you can actually access objects like arrays. obj['key1'] will return the same value as obj.key1.

Now that you have a list of those keys, and you know which letter, A, B, C, etc. corresponds to which key (user_file[0][letter]) you can actually iterate through these keys and use a temporary object to store the information (tmp[user_file[0][letter]] = user_file[i][letter]).

But what if user_file[i][letter] doesn't exist? This is where one of the coolest features about javascript comes into play: truthiness. When an object doesn't have a property and you try to access it, it will return a falsy value (either null or undefined, I can't remember off the top of my head).

So how do you apply the truthiness? Well you can kinda treat it like a boolean (kinda) and actually use an || operation on it. This is a technique called coalescing.

tmp[user_file[0][letter]] = user_file[i][letter] || "";

To put this line in simple terms if user_file[i][letter] doesn't exist use "" instead.

Awesome that's a cool line but what next? Well now we just have to put everything together.

If you place your first loop in a loop that iterates through the rest of the object array, we can do it all right there:

for(var i = 1; i < user_file.length; i++) {
    var tmp = {};
    for(var v in user_file[0]) {
      if(user_file[0].hasOwnProperty(v)) {
        tmp[user_file[0][v]] = user_file[i][v] || "";
      }
    }
   a.push(tmp);
}

And there you have it, an object array from values in objects later than the first element, using the first element's values as the keys, even if the later elements don't have values that correspond with those keys.

Patrick Barr
  • 1,123
  • 7
  • 17
1

You could iterate only the elements from index 1 and greater and take the object at index zero as blue print for the objects.

var data = [{ A: "Name", B: "Type", C: "Company", D: "Year", E: "Alt1", F: "Name1", G: "Alt2", H: "Name2", I: "Notes" }, { A: "Skittles", C: "Mars", D: 0, E: "Cadbury", F: "Sour Patch Kids", I: "CLEAN" }, { A: "Love", B: "Chocolate", C: "Dove", D: 0, E: "0", F: "0", }],
    keys = Object.keys(data[0]),
    result = data.slice(1).map(function (o) {
        var temp = {};
        keys.forEach(function (k) {
            temp[data[0][k]] = k in o ? o[k] : '';
        });
        return temp;
    });

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • This is so beautiful... I haven't used `.map` before - gotta learn how it works, but apart from that this makes perfect sense. I like the ternary expression too... nice and clean! Thank you! – Brian Powell Mar 22 '17 at 20:57
  • Can you explain why you end your expressions with a `,` instead of a `;`? I'm not sure what the difference is between these two.. I've always ended an expression with a semi-colon `console.log("hi");` – Brian Powell Mar 22 '17 at 21:02
  • do you mean the declaration listing? the variable with `var` keyword are usually separated by comma. – Nina Scholz Mar 22 '17 at 21:10
  • ahh okay - so it's just just a way to declare multiple vars without retyping `var` on each line? e.g. `var x = 5, y = 6, z = 7` instead of `var x = 5; var y = 6; var z = 7`? – Brian Powell Mar 23 '17 at 15:27
  • 1
    right. see syntax of [`var` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var) – Nina Scholz Mar 23 '17 at 15:32
1

You can simplify the process by using Array methods like forEach and map in conjunction with Object.keys.

var array = [
    {
        "A": "Name",
        "B": "Type",
        "C": "Company",
        "D": "Year",
        "E": "Alt1",
        "F": "Name1",
        "G": "Alt2",
        "H": "Name2",
        "I": "Notes"
    },
    {
        "A": "Skittles",
        "C": "Mars",
        "D": 0,
        "E": "Cadbury",
        "F": "Sour Patch Kids",
        "I": "CLEAN"
    },
    {
        "A": "Love",
        "B": "Chocolate",
        "C": "Dove",
        "D": 0,
        "E": "0",
        "F": "0",
    }
]

var master = array.shift()

master = Object.keys(master).map(function (k) { return [k, this[k]] }, master)

array.forEach(function (e) {
  master.forEach(function (pair) {
    var value = pair[0] in e ? e[pair[0]] : defaultValueFor(pair[0])
    delete e[pair[0]]
    e[pair[1]] = value
  })
})

console.log(array)


function defaultValueFor (key) {
  return key === 'Year' ? 0 : ''
}
gyre
  • 16,369
  • 3
  • 37
  • 47
1

Here's a step-by-step breakdown of one way of getting this done. This does have the advantage of not needing any ternary or if blocks...

var user_file = [
    {
        "A": "Name",
        "B": "Type",
        "C": "Company",
        "D": "Year",
        "E": "Alt1",
        "F": "Name1",
        "G": "Alt2",
        "H": "Name2",
        "I": "Notes"
    },
    {
        "A": "Skittles",
        "C": "Mars",
        "D": 0,
        "E": "Cadbury",
        "F": "Sour Patch Kids",
        "I": "CLEAN"
    },
    {
        "A": "Love",
        "B": "Chocolate",
        "C": "Dove",
        "D": 0,
        "E": "0",
        "F": "0",
    }
];
// split off the first set of keys
var keys = user_file[0];
// keep the data separate
var data = user_file.splice(1);
// iterate over the data
var result = data.map(function (item) {
   // item is now one of the last two items
   // get the keys for that item
   var itemKeys = Object.keys(item); 
   // Make a blank
   var newItem = {};
   // iterate over the keys
   itemKeys.forEach(function (key) {
      // get the "real" key name
      var realKey = keys[key]; 
      // set the value of that key to the value of the item
      newItem[realKey] = item[key];
   });
   // add to result
   return newItem;
});
console.log(result);
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
1

There is probably a more efficient way of doing this, but this is something I threw together that will work.

var data = [{
  "A": "Name",
  "B": "Type",
  "C": "Company",
  "D": "Year",
  "E": "Alt1",
  "F": "Name1",
  "G": "Alt2",
  "H": "Name2",
  "I": "Notes"
}, {
  "A": "Skittles",
  "C": "Mars",
  "D": 0,
  "E": "Cadbury",
  "F": "Sour Patch Kids",
  "I": "CLEAN"
}, {
  "A": "Love",
  "B": "Chocolate",
  "C": "Dove",
  "D": 0,
  "E": "0",
  "F": "0",
}];

var keyMap = data.shift();
var templateObj = {
  "A": '',
  "B": '',
  "C": '',
  "D": '',
  "E": '',
  "F": '',
  "G": '',
  "H": '',
  "I": '',
};
var results = [];

data.forEach(function(item) {
  var tmpResult = Object.assign({}, templateObj, item);
  var processedItem = {};
  for (var prop in tmpResult) {
    processedItem[keyMap[prop]] = tmpResult[prop];
  }
  results.push(processedItem);
});

console.log(results);
Brian Glaz
  • 15,468
  • 4
  • 37
  • 55