I had a conversation on IRC the other day with Jason Dean about a shortcoming of arraySet().
The issue is that array set works like this:
arraySet(array, from, to, value)
So like this:
sameStructs = [];
arraySet(sameStructs, 1, 5, {});
sameStructs[3].foo = "bar";
writeDump(sameStructs);
Unfortunately - as you might guess from the variable name - ColdFusion only evaluates the value once - meaning each array element has the same structure. So this code outputs:
array | |||||
---|---|---|---|---|---|
1 |
| ||||
2 |
| ||||
3 |
| ||||
4 |
| ||||
5 |
|
Railo does this a bit better, by creating a new struct for each element (I imagine by duplicating the input value):
Array | ||||||||
1 |
| |||||||
2 |
| |||||||
3 |
| |||||||
4 |
| |||||||
5 |
|
So if you were on Railo, the rest of this article is perhaps a waste of time. I'm working around a ColdFusion CFML shortfall here.
What ColdFusion needs is an arraySetEach() function, which is a hybrid of arraySet(), and arrayEach(). So that each element being set take the return value of a callback function. Here's one I'm gonna submit to CFLib (and being the moderator, I suspect it'll get approved, too ;-) :
array function arraySetEach(required array array, required numeric from, required numeric to, required function callback){
arrayset(array, from, to, "");
for (var i=from; i <= to; i++){
array[i] = callback(index=i, argumentCollection=arguments);
}
return array;
}
Easy. Instead of giving the function just a value to populate each array element with, we pass it a function, and each array element gets the return value from the function. Here's a basic example using each of arraySet() and arraySetEach():
uuids = [];
arraySet(uuids, 1, 5, createUuid());
writeDump(uuids);
uuids = arraySetEach([], 1, 5, function(){
return createUuid();
});
writeDump(uuids);
array | |
---|---|
1 | FA2D3162-AECF-4E3B-3F01F31D1129D67D |
2 | FA2D3162-AECF-4E3B-3F01F31D1129D67D |
3 | FA2D3162-AECF-4E3B-3F01F31D1129D67D |
4 | FA2D3162-AECF-4E3B-3F01F31D1129D67D |
5 | FA2D3162-AECF-4E3B-3F01F31D1129D67D |
array | |
---|---|
1 | FA2D3CE6-0DE8-A6C4-852ABAA06B006B63 |
2 | FA2D3CEF-EE50-E2CB-FD9C4579784BA59E |
3 | FA2D3CF7-E1CE-C4D2-10FE504DBE2A1256 |
4 | FA2D3CFE-BFFD-FCCC-DB947FC2B556C379 |
5 | FA2D3D06-E1BB-EE43-FF6D0501E8831384 |
To belabour the point: arraySet() calls createUuid() once, and uses that value for each element. Whereas arraySetEach() calls createUuid() for each element.
This code is, btw, a good example of where having CFML inbuilt functions being first-class functions would be useful. Ideally I would not need to wrap createUuid() in a bespoke function to use it as a callback:
function(){
return createUuid();
}
I should just be able to do this:
uuids = arraySetEach([], 1, 5, createUuid);
Where createUuid is itself the callback. Adobe is apparently adding this in ColdFusion 11.
Here are some more proofs of concept:
sequence = arraySetEach([], 1, 5, function(){
return index;
});
writeDump(sequence);
fromList = arraySetEach([], 1, 5, function(){
return listGetAt("tahi,rua,toru,wha,rima", index);
});
writeDump(fromList);
array | |
---|---|
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
array | |
---|---|
1 | tahi |
2 | rua |
3 | toru |
4 | wha |
5 | rima |
So this demonstrates, I s'pose, how this approach would benefit Railo CFML too: instead of just providing a dumb value to set in the array, one can have any functionality one wants.
Anyway, I'm at the bar @ CFCamp, and people want to talk to me so at this fairly disconnected juncture, I'm gonna "press send". I'll put a draft of that function up on CFLib, and link back.
I've raised a ticket to get this added to CFML: 4022432.
--
Adam