Data extracting question

guys, i have payload like this:

my goal is to pass only those hours where OnOff: “1” and show them in ha as “12.00, 13.00, 14.00”
any help is appreciated. thank you!

You can use the split node to split the array in to seperate messages.
You then use the single messages to format it in the way you want, and can then use the join node to merge them all back together again if required.

thanx. i figured i can use switch node to filter those “onoff:1” messages. but how i should wrote property line?
image

As your message for onOff seems to be

“1”

instead of an integer

1

You probably need to switch from numbers to string:

image

ok. but still property line?

You can simply copy the line from the debug log by pressing this button:
image

yeah, but there are like 48 lines of those. should i make 48 switch nodes? :o

As I said, you first need to split the messages. They whole array of 48 values will then split into 48 messages. These will all have the same layout = work with your one switch node.
You then use your switch node to filter these single messages and then you can either merge them back together into one array or simply use the messages seperate.

i have no experience with split node. adding split node giving me bunch of payloads and i have no idea what to do next

This explains the basics: Split text into one message per line : Node-RED

The split node always splits the msg.payload array into seperate messages.
In your specific case, you actually only care about the msg.payload.hours array.

So first, you can start by changing the message with the change node.
This is simply so that the msg.payload is directly your “hours array”.
image

You connect that to the split node, and you will get 48 messages that will contain the values from the arrays:
“price”, “onOff”, “start” & “saving”

You can then manipulate, filter, change these seperate messages to your hearts content, and use them in the following flow.

thanx! now i have separate messages, but i cant get join node to work.
payload:
image

split node

switch
image

and join
image

Like you, I have a msg.playload that is an object, with one value (results) being an array of many items, and I want to get something out of some of the individual items in the array.

Working backwards from what you want to end up with, I believe you just want a string that is a list of hours for which onOff is 1. My plan would be to start with an empty results string, go through the array, and for each element I want, add the hour (as a string value) to the end of the results string.

There are many ways to do this. If you just want to make a change to each/some items in the array, then the split node is a good way to go. However as I really want to keep the ‘returnString’ from one array element to the next, I need to save it somewhere as I go along. Yes, this is possible using flow context variables, but in many cases like this resorting to a function node is simply much easier. It is nice to use split/join nodes, but I have found that the weakness in Node-RED is performing iteration simply and neatly.

Here is my function node to do something similar:

I have started the return string I want with 'Dates: " - if there are no values in the array I can see it has worked but returned ‘nothing’, better than just a blank.

I iterate over the array using the simple ‘i’ approach.
If my consumption is more than 50, I pick out the date from the interval string, and then I add to to the back of the returnString with a ", ".

At the end, as long as I have added something in the loop, the return string will have an extra ", " on the end, so I chop that off.
Put the result back into msg.payload, and I get the months for which my gas consumption is over 50 cubic metres.

I am sure that you can change the variables to suit your requirement.
And change the substring parameters (start, length) to pick out hours in your timestamp.

… and as you have just found, if you split an array with ‘split’ and then drop some of the items, ‘join’ will not work (set it to manually join, and 2 seconds after the last message…)

I’m afraid that sometimes, writing a function node is just much easier!

ooo, would you mind copy your code?

OK. So I have changed to the code for your object. I can only partially test this, so it may need a bit of a tweak if it does not work.

let returnString = "Hours: ";
let hourString = "";

for (var i = 0; i < msg.payload.hours.length; i++) {
    if (msg.payload.hours[i].onOff == "1") {
        hourString = msg.payload.hours[i].start;            // get ISO string
        hourString = new Date(hourString);                  // turn into date object
        hourString = hourString.toTimeString();             // get time (as local)
        hourString = hourString.substring(0, 5);            // pick out hours
        returnString = returnString + hourString + ", ";
    }
}
if (returnString.length > 7) {
    returnString = returnString.substring(0, returnString.length - 2);
}
msg.payload = returnString;

return msg;

You should be able to copy & paste this into a function node.

It is looking for onOff as “1”, and will pick out the start timestamp, which is an ISO string. I note that your dates are coming up as +03:00 hours, which is 3 hours ahead of UTC.

The code then creates a new Date object, using the string.
Then it picks out the time - this should do so using your computer local settings and thus get the time as local time to you.
Then it picks out the first five characters, ie hh:mm

Times and dates are always a challenge to work with, and I assume you want your output in local time!

To test, I created a short object in an inject node. The on start is 16:00+03:00, which is 3 hours ahead of UTC, so would be 13:00 in UTC. I am in the UK, which is currently on DST so one hour ahead of UTC, and the date has popped out as 14:00.
Therefore I think it works!

Good luck with your coding.

wow! THANK YOU! its working! thanx-thanx-thanx :slight_smile:
one more thing… how would be separate todays and tomorrows times, a’la TODAY hours: 8.00, 9.00 / TOMORROW hours 9.00, 10.00… and if there is no hours passing filters then just “nothing” or smtg like that.

Getting more complicated…

const returnArray =[];                                      // return an array of string times, one for each date found
let returnString = "";
let hourString = "";
let dateIs = msg.payload.hours[0].start.substring(0,11);    // note date for first item
let dateIndex = 0;

for (var i = 0; i < msg.payload.hours.length; i++) {
    // check date, if changed bump return index, start a new return string, and capture data again
    if (msg.payload.hours[i].start.substring(0,11) > dateIs) {
        dateIndex++;
        returnString = "";
        dateIs = msg.payload.hours[i].start.substring(0,11)
    }
    if (msg.payload.hours[i].onOff == "1") {
        hourString = msg.payload.hours[i].start;            // get ISO string
        hourString = new Date(hourString);                  // turn into date object
        hourString = hourString.toTimeString();             // get time (as local)
        hourString = hourString.substring(0, 5);            // pick out hours
        returnString = returnString + hourString + ", ";
        returnArray[dateIndex] = returnString;              // save to return array for this date
    }
}

// to remove trailing ", " we need to go through return array
for (i = 0; i < returnArray.length; i++){
    if (returnArray[i].length > 0) { returnArray[i] = returnArray[i].substring(0, returnArray[i].length -2)}
}
msg.payload = returnArray;

return msg;

You will have to work out what I have changed yourself :wink:

Just to prove that it does actually work (at least for me)

This could be better written, so I will leave that for you to work through and fix.

hmmm… im getting “TypeError: Cannot read property ‘length’ of undefined”
i have no idea what to change :confused:

It is always the exception conditions that cause the problems.

I’ll leave you to work out a) what I have changed, b) why I needed to change it, and c) why the change fixes the issue.

const returnArray =[];                                      // return an array of string times, one for each date found
let returnString = "";
let hourString = "";
let dateIs = msg.payload.hours[0].start.substring(0,11);    // note date for first item
let dateIndex = 0;

for (var i = 0; i < msg.payload.hours.length; i++) {
    // check date, if changed bump return index, start a new return string, and capture data again
    if (msg.payload.hours[i].start.substring(0,11) > dateIs) {
        if (returnString.length>0) {dateIndex++};
        returnString = "";
        dateIs = msg.payload.hours[i].start.substring(0,11)
    }
    if (msg.payload.hours[i].onOff == "1") {
        hourString = msg.payload.hours[i].start;            // get ISO string
        hourString = new Date(hourString);                  // turn into date object
        hourString = hourString.toTimeString();             // get time (as local)
        hourString = hourString.substring(0, 5);            // pick out hours
        returnString = returnString + hourString + ", ";
        returnArray[dateIndex] = returnString;              // save to return array for this date
    }
}

// to remove trailing ", " we need to go through return array
for (i = 0; i < returnArray.length; i++){
    if (returnArray[i].length > 0) { returnArray[i] = returnArray[i].substring(0, returnArray[i].length -2)}
}
msg.payload = returnArray;

return msg;