Search and return a value from within a flow variable?

Hello,

I've stored a number of values in a flow variable, example:

{
	"data": {
		"item_1": "value_1",
		"item_2": "value_2"
	}
}

From within the function node I'd like to search for a value and return the associated key, example:

A search for "value_1" would return "item_1"

Is this possible?

yes.

Literally thousands of posts on the internet - e.g...
https://www.google.com/search?q=js+search+object+for+value

Here is an example i knocked up

Hi Steve,

I think my example was pretty terrible. Here is the data I'm trying to search:

I want to return for example, "this_week" via "BTC-USD-190426" which I have as part of a payload.

i.e. A search for "BTC-USD-190426" will return "this_week"

I've tried pretty much every example I can find over the last few hours, with no luck :frowning:

If you set msg.target to the value you want to search for (BTC-USD-190426), then you can use a Change node to evaluate the following JSONata Expression:

$keys(payload.*.$sift(function($v, $k) {$v = $$.target}))

This assumes the object you've shown the screenshot of lives under msg.payload.

Hi,

How would I setup the change node, I'll give this a try, thank you :slight_smile:

Urgh, 5 hours in, must have tried 100 possibilities to search through this data and return the right result. Close to giving up... :frowning:

The JSON appears to be:

{
	"BTC": {
		"USD": {
			"this_week": "BTC-USD-190426",
			"next_week": "BTC-USD-190503",
			"quarter": "BTC-USD-190628"
		}
	},
	"LTC": {
		"USD": {
			"this_week": "LTC-USD-190426",
			"next_week": "LTC-USD-190503",
			"quarter": "LTC-USD-190628"
		}
	},
	"ETH": {
		"USD": {
			"this_week": "ETH-USD-190426",
			"next_week": "ETH-USD-190503",
			"quarter": "ETH-USD-190628"
		}
	},
	"ETC": {
		"USD": {
			"this_week": "ETC-USD-190426",
			"next_week": "ETC-USD-190503",
			"quarter": "ETC-USD-190628"
		}
	},
	"XRP": {
		"USD": {
			"this_week": "XRP-USD-190426",
			"next_week": "XRP-USD-190503",
			"quarter": "XRP-USD-190628"
		}
	},
	"BCH": {
		"USD": {
			"this_week": "BCH-USD-190426",
			"next_week": "BCH-USD-190503",
			"quarter": "BCH-USD-190628"
		}
	},
	"BSV": {
		"USD": {
			"this_week": "BSV-USD-190426",
			"next_week": "BSV-USD-190503",
			"quarter": "BSV-USD-190628"
		}
	},
	"EOS": {
		"USD": {
			"this_week": "EOS-USD-190426",
			"next_week": "EOS-USD-190503",
			"quarter": "EOS-USD-190628"
		}
	}
}

Basically, I need a compact method (ideally via a function node) to grab the key for a given value, i.e. "BTC-USD-190426", I tried to suggestion above with a change node but didn't have any luck, sadly.

The JSON you've shared here is different to the JSON you shared previously. So the JSONata expression I gave you won't work.

Here's an example flow that does work once the JSONata has been updated for your latest JSON format...

[{"id":"ba39e234.05ea2","type":"inject","z":"41b925f9.3c983c","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":220,"wires":[["eed0c1d6.1d737"]]},{"id":"eed0c1d6.1d737","type":"function","z":"41b925f9.3c983c","name":"","func":"msg.payload = {\n\t\"BTC\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"BTC-USD-190426\",\n\t\t\t\"next_week\": \"BTC-USD-190503\",\n\t\t\t\"quarter\": \"BTC-USD-190628\"\n\t\t}\n\t},\n\t\"LTC\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"LTC-USD-190426\",\n\t\t\t\"next_week\": \"LTC-USD-190503\",\n\t\t\t\"quarter\": \"LTC-USD-190628\"\n\t\t}\n\t},\n\t\"ETH\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"ETH-USD-190426\",\n\t\t\t\"next_week\": \"ETH-USD-190503\",\n\t\t\t\"quarter\": \"ETH-USD-190628\"\n\t\t}\n\t},\n\t\"ETC\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"ETC-USD-190426\",\n\t\t\t\"next_week\": \"ETC-USD-190503\",\n\t\t\t\"quarter\": \"ETC-USD-190628\"\n\t\t}\n\t},\n\t\"XRP\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"XRP-USD-190426\",\n\t\t\t\"next_week\": \"XRP-USD-190503\",\n\t\t\t\"quarter\": \"XRP-USD-190628\"\n\t\t}\n\t},\n\t\"BCH\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"BCH-USD-190426\",\n\t\t\t\"next_week\": \"BCH-USD-190503\",\n\t\t\t\"quarter\": \"BCH-USD-190628\"\n\t\t}\n\t},\n\t\"BSV\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"BSV-USD-190426\",\n\t\t\t\"next_week\": \"BSV-USD-190503\",\n\t\t\t\"quarter\": \"BSV-USD-190628\"\n\t\t}\n\t},\n\t\"EOS\": {\n\t\t\"USD\": {\n\t\t\t\"this_week\": \"EOS-USD-190426\",\n\t\t\t\"next_week\": \"EOS-USD-190503\",\n\t\t\t\"quarter\": \"EOS-USD-190628\"\n\t\t}\n\t}\n};\n\nmsg.target = \"EOS-USD-190426\"\nreturn msg;","outputs":1,"noerr":0,"x":230,"y":220,"wires":[["cfdb4b36.94a9e8"]]},{"id":"cfdb4b36.94a9e8","type":"change","z":"41b925f9.3c983c","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$keys(payload.*.*.$sift(function($v, $k) {$v = $$.target}))","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":220,"wires":[["3064c623.ae541a"]]},{"id":"3064c623.ae541a","type":"debug","z":"41b925f9.3c983c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":220,"wires":[]}]

@swiftnesses, you could have learned about recursion or JSONata in that 5hrs :slight_smile:

@knolleary I must spend some time looking into JSONata - seems to be very powerful.

@swiftnesses
In case you do want this as a function - or - you change the data structure (for a 4th time) then this function should work for all situations (unless you add arrays)

See my recursive object search function in action here

The code...

function deepSearchObject(theObject, theValue) {
    var result = null;
    for(var prop in theObject) {
        if(theObject[prop] == theValue) {
          return {prop:prop, path:prop}; 
        }        
        if(theObject[prop] instanceof Object) {
            result = deepSearchObject(theObject[prop], theValue);
            if(result){
              result.path = prop + "." + result.path
              return result;
            }
        }          
    }
    return result;
}
var result = deepSearchObject(data,"EOS-USD-190503");
console.log("EOS-USD-190503 is in property : ", result.prop);
console.log("EOS-USD-190503 full path = ", result.path);

notes...

  • there WILL be bugs (it is barely tested)
  • i don't handle arrays (will leave that to you)
  • if theObject is not an object, it wont work
  • searching for undefined or null is not supported
  • searching for falsey values will likely give odd results (unless you change the comparitor to ===)

Thank you everyone, used @knolleary's solution in the end and spend some time learning JSONata (which should come in handy!).

1 Like

given that the value you have already contain parts of the search path (the "EOS-USD" part, you can simply do:

var flatSearch = (theObject, theValue)=>{
  var part = theValue.split("-")
  var result = null
  var interrestingPartOfObject = theObject[part[0]][part[1]]
  for(prop in interrestingPartOfObject){
    if(interrestingPartOfObject[prop] === theValue){
      return {prop: prop, path: `${part[0]}.${part[1]}.${prop}`}
    }
  }
}

var result = flatSearch(data,"EOS-USD-190503")

the solution Steve gave is more general, but for your case, this function will be much faster for large objects.

I did consider short-cutting as you have here theObject[part[0]][part[1]] however the spec of the data changed 3 times so thought a generic solution may be advisable.

In fact, as the last part seems to be a date (190503) we could probably have provided a simpler (non scanning) logical solution - but we have to assume the provided data may change.

1 Like