Pr0no
Pr0no

Reputation: 4099

Determine if array element represents an object or a value

From a JSON object (containing stock data), I want to add certain elements to an array (in Google Sheets script editor):

var quote = JSON.parse(response.getContentText());

// Example of what quote object looks like:

{
    "quoteSummary": {
        "result": [
            {
                "Profile": {
                    "sector": "Technology",
                    "website": "www.test.com"
                },
                "Owners": [
                    {
                        "name": "Eric",
                        "age": "28"
                    },
                    {
                        "name": "Susan",
                        "age": "44"
                    }
                ],
                "Profit": 100,
                "Assets": 7000
            }
        ]   
    }
}

Here is my current approach to read only some specific values:

var arr   = [];

arr.push(quote.quoteSummary.result[0].Profile.sector); // Technology
arr.push(quote.quoteSummary.result[0].Owners[1].name); // Susan
arr.push(quote.quoteSummary.result[0].Profit);         // 100

But since there are many specific properties to read, I'd like to use a loop:

var quote = JSON.parse(response.getContentText());
var arr   = [];

var el = [
    ['Profile', 'sector'],
    ['Owners[1]', 'name'],
    ['Profit']
];

for (i = 0; i < el.length; i++)
{
    if (quote.quoteSummary.result[0][el[i][0]][el[i][1]] !== undefined)
    {
        arr.push(quote.quoteSummary.result[0][el[i][0]][el[i][1]].value);
    }
}

/*
Expected output (if I would loop through arr):
Technology
Susan
100
*/

The point is that different stocks, will have different properties. So el might define some non-existing elements or properties. Assume (in a bit of an other way of defining el -- as I wrote, I'm plexible here.. perhaps the paths are the easiest):

var el = [
    'Profile.website',
    'Profile.name',
    'Assets'
]

/*
Expected output:

www.test.com
                 <----- "name" doesn't exist!
7000

Notice that in this example, there is no property "name" in Profile,
so I'd like to add an empty element to arr
*/

But this does not work. What is a generic loop that accomplishes what I'm trying to do here? The array defining what I want can also be constructed differently if that helps. But the point is that I don't end up with a script like:

arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);
arr.push(quote.quoteSummary.result[0].Profile.something);

Upvotes: 4

Views: 93

Answers (2)

Gershom Maes
Gershom Maes

Reputation: 8150

I recommend you use variable-length chains of property names. Each name in a given chain represents a deeper property. You can "dive" into an object through an arbitrary number of property names with code like this:

let dive = (obj, propertyNames) => {
  for (let pn of propertyNames) obj = obj[pn];
  return obj;
};

Now you can say:

let dive = (obj, propertyNames) => {
  for (let pn of propertyNames) obj = obj[pn];
  return obj;
};

let quote = {
  quoteSummary: {
    result: [
      {
        Profile: {
          sector: 'Technology',
          website: 'www.test.com'
        },
        Owners: [
          {
            name: 'Eric',
            age: '28'
          },
          {
            name: 'Susan',
            age: '44'
          }
        ],
        Profit: 100,
        Assets: 7000
      }
    ]
  }
};

// Here are the "variable-length property chains":
let el = [
  [ 'Profile', 'sector' ],
  [ 'Owners', 1, 'name' ],
  [ 'Profit' ]
];

// Here's how to combine `el`, `dive`, and your `quote` data to get a result:
let arr = el.map(propertyNames => dive(quote.quoteSummary.result[0], propertyNames));

console.log(arr);

You could even replace dive with Array.prototype.reduce, if you'd like to stay functional but avoid the function definition:

dive(someObj, propertyNames);

is equivalent to

propertyNames.reduce((obj, propName) => obj[propName], someObj);

Note the above code all assumes that a property exists for each term in the property chain (except the final property name, which may resolve to undefined without causing any errors). If some cases may have the, e.g., Profile key undefined or null you'll need to write some kind of if (propertyDoesntExist) / else statement which describes how to deal with missing properties.

For example you could modify dive to handle non-existent properties:

let dive = (obj, propertyNames, valueIfMissing=null) => {
  for (let pn of propertyNames) {
    // Intentional use of loose `==` operator!
    if (obj == null) return valueIfMissing;
    obj = obj[pn];
  }
  return obj;
};

This means that:

dive({ a: 1, b: 2, c: 3 }, [ 'd' ]) === null;

But we can substitute any default return value:

dive({ a: 1, b: 2, c: 3 }, [ 'd' ], 'ddd') === 'ddd';

Note this works at any depth:

dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ]) === null;
dive({ a: { a: 1 }, b: { b: 2 }, c: { c: 3 }, d: null }, [ 'd', 'd' ], 'ddd') === 'ddd';

Upvotes: 2

Alex Collette
Alex Collette

Reputation: 1784

You should be able to do this:

if(y.value){
  arr.push(y.value);
} else {
  arr.push(y);
}

However, this will break if y.value happens to be something like 0, false, etc. If this is the case you can do this:

if(y.hasOwnProperty("value")){
  arr.push(y.value);
} else {
  arr.push(y);
}

Based on the comment:

if (quote.quoteSummary.result[0][el[i][0]].hasOwnProperty("value")) { 
    arr.push(quote.quoteSummary.result[0][el[i][0]].value);
} else {
    arr.push(quote.quoteSummary.result[0][el[i][0]]);
}

Upvotes: 1

Related Questions