I am trying to take an array of structs with vario...
# cfml-general
d
I am trying to take an array of structs with various keys and be able to filter each struct to only return a subset of the data. For example:
Copy code
aPartners = [
  {
   "partnerid":1,
   "partnername": "Partner 1",
   "partnercode": "pt1",
   ...
   "configsettings: {
     ...
   } 
  },
  {
   "partnerid":2,
   "partnername": "Partner 2",
   "partnercode": "pt2",
   ...
   "configsettings: {
     ...
   } 
  }
];
allowedKeys = ["partnername","partnercode"];
How to process the above (arrayReduce/Map etc) to produce an array of structs with just partnername and partnercode in each struct. I tried this but it doesn’t work as needed:
Copy code
allowedKeys = ["partnername","partnercode"];
for(partner IN aPartners) {
    objKeys = structKeyArray(partner);
	result = objKeys.reduce(function(accumulator, ele)  {
        if( allowedKeys.findNoCase(ele) ) {
			obj = {
				"#ele#": partner[ele]
			};
			return accumulator.append(obj);
		} else {
		    return accumulator;
		}
	}, []); 
};
What should I be using?
t
If you're starting with an array, and aiming to get out an array of the same size, then you should be using arraymap.
1
Reduce is for starting with an array and getting a value of a different type, or a larger array.
d
Ok, will give thqt a go. I always seem to start trying to use reduce when I need to use map.
t
Inside the map closure, you probably use structfilter to get a smaller struct.
👍 1
a
Do the structs always have the keys that you want to map across? If so... no need to filter, just return the specific struct, eg: https://trycf.com/gist/4125fd967a56e8f71f0dadb2afd69452/acf2021?theme=monokai NB: it always helps ppl help you if your sample data is syntactically correct / complete.
d
Yeah, sorry, added the ellipsis to indicate presence of other keys.I am exploring an idea for our API to allow a client to request a subset of fields that would normally be returned by the endpoint. So an organisations endpoint that defaults to return an array of organisations could be asked to just return the organisation name and postcode for example.
The structs will always have the keys but I need to return an array of structs where each struct only contains the keys (and values) requested in the allowedKeys array.
I have actually figured out how to do it - I had missed out a final step. I was looping the original array and then using reduce on the struct in the loop. Then returning the reduced struct. I had forgotten to append the new filtered struct to a new array to create the filtered array or structs. I’ll try the arrayMap option as well to see which works better.
a
what did you come up with? I got this: https://trycf.com/gist/32055f68b067ae3dae95881b3f956c51/lucee5?theme=monokai The key bit is this:
Copy code
justTheImportantStuff = aPartners.map(
    (partner) => allowedKeys.reduce(
        (subStruct, key) => substruct.insert(key, partner[key]),
        {}
    )
)
d
So far, just tweaked my original: https://trycf.com/gist/d42c6d88d5b4441b2f7e2b4b87923896/lucee5?theme=monokai Yours looks a lot more straightforward.
I really like the use of map here. My for loop seems overkill to achieve the same result - thanks for the demo.
And now converted to a util function that I can then use across the API:
Copy code
public array function filterAllowedObjectProperties(
	required array objArray,
	array allowedProperties=[]
) {
	var loc = {};
	if(!arrayLen(arguments.allowedProperties)) {
		return arguments.objArray;
	}
	loc.allowedProps = arguments.allowedProperties;
	loc.filteredProps = arguments.objArray.map(
	    (obj) => loc.allowedProps.reduce(
			(subStruct, key) => subStruct.insert(key, obj[key]),
			{}
		)
	)
	return loc.filteredProps;
}
a
The whole
var loc
thing is a bit "CF9". You know there's a
local
scope now, yeah?
but yeh, good other than that 😛
d
Old habits!
😂 1
a
and actually... if I may... you don't need any of your intermediary variables in there anyhow!
Copy code
public array function filterAllowedObjectProperties(
	required array objArray,
	array allowedProperties=[]
) {
	if(!arguments.allowedProperties.len()) {
		return arguments.objArray;
	}
	return arguments.objArray.map(
	    (obj) => allowedProperties.reduce(
			(subStruct, key) => subStruct.insert(key, obj[key]),
			{}
		)
	)
}
(I've also switched from
arrayLen
to just
len
as the rest of the code is using obj.method not function(obj), so keeping it uniform; but that's a small thing)
d
Perfect! Many thanks for the updated example - implemented. Just tried the concept on one of our public endpoints that returns a bunch of news stories with a lot of data. 10 stories returns approx 55k of data. Add the fields filter is able to reduce it down depending on the data that is actually required. If I just need the news headline and associated organisation I can reduce the request size down to 2.3k of data.
👍 1