.Length and Array of Objects ? What am i doing wrong?

Guys,

I have an array of objects - i wish to work my way through them, extract a certain property from each object and then sum that (plus get the length) so i can then get a running average

I have found some (what i thought) were good examples and tried to use them by building them up slowly - but not a lot of joy - could i get a pointer where i am going wrong here please

Here is an image of the array of objects and the Function code - i started with let rather than var but they seem to make no difference

I then setup the msg.Solar bit just to see what was coming out in the object and it was referencing it correctly from the Global context

And here is the debug

Just need to understand why the .length is not working

Craig

add a node.warn(SolarCurrentBids); to see what is in it.

try this

let amber = global.get("amber")
let bids = amber["SolarCurrentBids"]
node.warn(bids.length)

In my experience, I have never been able to get a portion of an object directly in a global.get function. Thus I think the trick would be replace:

var SolarCurrentBides = global.get('Amber.SolarCurrentBids')

with

var AmberVariable = global.get('Amber')
var SolarCurrentBids = 'Amber.SolarCurrentBids'

Do however keep in mind that AmberVariable and SolarCurrentBids are getting passed a reference so if you were to modify the values, those would be reflected in the global variable. Not really an issue in the code shown, but unless you pay attention, this can bite you in other uses of context variable that are objects

OK adding in the Node.warn - shows this - so that seems to be working as expected in terms of getting the global variable.

image

And then after adding in the extra code

So it does look like it needs to be a two step process - will continue using this approach and report back

Thanks for the tip

Craig

Yep thanks for the pointer re the reference passing - i only intend to use this to read so no dramas there

Craig

OK getting closer i think now

Now seem to have a problem with using a counter to iterate through array of objects

See screen shot below - not sure if this is still related to the first issue - it appears to be in some way ?

It appears based on the debug window that the Value of I is being incremented by the number of objects in the array so this appears to work.

However when trying to use "i" as the index to access the objects in the array it seems to not work correctly - based on the node.warn for the items object reference ?

Craig

I would replace

node.warn("Items = " + item);

with

node.warn("Items time = " + item.Time);
node.warn("Items sale price = " + item.SolarSalePrice);

to see in the debug output what it thinks is inside the item object. This will probably tell you what is the item.SolarSalePrice you are using in your math.

Yep well that seems to know what it is and iterates through them

So i will keep playing

and i will let you know

Craig

Now that I look closer, I am pretty sure the issue is with using item.found in line 17 the if statement. I am unclear what you are trying to test for, but since found isn't a component of the object, I don't think you are ever incrementing the itemsFound quantity, thus it is barfing on dividing by 0.

Thanks for hanging in there.

I am trying to test that the object is valid (as it gets overwritten and has no values at the Hour and 1/2hour)

I think you were on the rigth track with the test that you did for the sale price etc

It seems that there are two items that are making up the items and this is where i am having problems.

WIll keep working

Craig

Have a look at Array.reduce(). That should do most of the hard work for you. JavaScript Array reduce() Method

What would the object look like during times when there aren't valid values? If the array would be empty then the loop would not happen and no test is needed. Otherwise, a sample of the object in the array would allow me to see what the conditional test would be.

Hey Colin,

Thanks for that - i can not see any indication that will work for an array of objects though ?

Craig

Yeah i will need to go back and look at the code that clears out the Array at the end of each 1/2 hour period - i have a feeling it actually wipes out the whole array and the objects - so nothing exists during that time.

WIll check on that and see if it is feasible to just blank all the objects (or create a new blank array after destroying the old one)

Will report back.

THe new code is currently running with your suggestions and just throws an error (but keeps on trucking) for the first run after the hour and 1/2 hour

Craig

Try something like this

// check SolarCurrentBids is an array
if (Array.isArray(SolarCurrentBids)) {
    msg.payload = SolarCurrentBids.reduce(accumulatePrice, {sum: 0, count: 0})
} else {
    // not an array so ignore it
    msg = null
}
return msg;

function accumulatePrice(accumulator, item) {
    accumulator.sum += item.SolarSalePrice
    accumulator.count += 1
    return accumulator
}

Example flow:

[{"id":"036757c5864ddad1","type":"inject","z":"bdd7be38.d3b55","name":"Sample data","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"SolarSalePrice\":10,\"Time\":\"11:01\"},{\"SolarSalePrice\":15,\"Time\":\"11:02\"},{\"SolarSalePrice\":20,\"Time\":\"11:03\"}]","payloadType":"json","x":140,"y":1960,"wires":[["375d8bfa152df267"]]},{"id":"0d3a0d62a22f73f0","type":"debug","z":"bdd7be38.d3b55","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":1960,"wires":[]},{"id":"375d8bfa152df267","type":"function","z":"bdd7be38.d3b55","name":"Calc total price and count","func":"const SolarCurrentBids = msg.payload\n// check it is an array\nif (Array.isArray(SolarCurrentBids)) {\n    msg.payload = SolarCurrentBids.reduce(accumulatePrice, {sum: 0, count: 0})\n} else {\n    // not an array so ignore it\n    msg = null\n}\nreturn msg;\n\nfunction accumulatePrice(accumulator, item) {\n    accumulator.sum += item.SolarSalePrice\n    accumulator.count += 1\n    return accumulator\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":370,"y":1960,"wires":[["0d3a0d62a22f73f0"]]}]

Thanks Colin,

That looks nice and elegent.

I will play with that this evening and report back.

Craig

Here is the final code based on what Colin did for me - works fine thanks

Some nice learning along the way in terms of not being able to use the .length parameter on an Array of objects and digging down into arrays

[{"id":"f101e22e33dfb987","type":"function","z":"469ddd52.4a9aa4","name":"Calc total price and count","func":"//Load values in from Current Global array \nlet Amber = global.get(\"Amber\");\nlet SolarCurrentBids = Amber[\"SolarCurrentBids\"];\n//node.warn(SolarCurrentBids.length)\n\n// const SolarCurrentBids = msg.payload\n// check it is an array\nif (Array.isArray(SolarCurrentBids)) {\n    msg.SumAndCount = SolarCurrentBids.reduce(accumulatePrice, {sum: 0, count: 0})\n    msg.AverageAmberSolarFeedPrice = msg.SumAndCount.sum/msg.SumAndCount.count\n    \n} else {\n    // not an array so ignore it\n    msg = null\n}\nreturn msg;\n\nfunction accumulatePrice(accumulator, item) {\n    accumulator.sum += item.SolarSalePrice\n    accumulator.count += 1\n    return accumulator\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":650,"y":180,"wires":[["8e99cf6ad8bf6522","d6b579ec.367928"]]}]

regards

Craig

You can most definitely use .length on an array of objects.

msg.SumAndCount = SolarCurrentBids.reduce(accumulatePrice, {sum: 0, count: 0})
    msg.AverageAmberSolarFeedPrice = msg.SumAndCount.sum/msg.SumAndCount.count

Unless you want to return the SumAndCount in the message then that would be better as
SumAndCount = ...
Also you might want to think about what to do if the array is present but it is empty. In that case the count returned by reduce will be zero and the divide will fail.
const

Yep i have taken care of that further along in the script last night - thanks for the hints though

Craig