Array formula in excel suddenly not working... troubleshooting - arrays

I am currently using this array formula..
{=LARGE(IF(('Data Input'!$L$3:$L$15000=$B10)*('Data Input'!$H$3:$H$15000>$C10),'Data Input'!$O$3:$O$15000,0),1)}
Where B10 is a text ID, like 658A and L:L is the column with the IDs.
C10 is a date, with H:H being the column with dates.
O:O being the column with the # value that I am retrieving.
This formula works fine with my purposes when used with ctrl,shift,enter
The problem arises when I try to use...
{=IF('Data Input'!$L$3:$L$15000=$B10,1,0)}
It always returns a FALSE result, even though it works correctly in the first formula.
What is different about the second formula that changes the results?
This is very strange to me.
Thanks for any help.

the IF is only comaring the first value of the array that is returned, so only if the first comparison is true, will it return a true value.
Example to illustrate:
formula
Formula:
{=IF(A1:A3=B2,1,0)} will; return 0, unless cell A1 is changed to true. To change the result to have it return true if any of the values are true, you have to resort to a little trickery...
First, use -- to change the True/False values to 1/0, then use SUM to add them together. as IF treats any non-zero result as true, this will result in 1 being returned when any comparison is true.
Working through our example with the new formula {=IF(SUM(--(A1:A3=B2)),1,0)} (still an array formula) we get the following steps in evaluation:
=IF(SUM(--(A1:A3=B2)),1,0)
=IF(SUM(--(A1:A3=2)),1,0)
=IF(SUM(--({1,2,2}=2)),1,0)
=IF(SUM(--({False,True,True})),1,0)
=IF(SUM(0,1,1),1,0)
=IF(2,1,0)
=1

Your second formula is, itself, returning an array. You are only viewing the top left element in that return array - which happens to be FALSE.
Your first formula returns a scalar value; that is the difference.
If you want to sum the '1' values then your second formula could be amended to
{=SUM(IF('Data Input'!$L$3:$L$15000=$B10,1,0))}
which is also a scalar return.

Related

trying to get the value of the struct inside an array

Looking for a one liner code either in java or cfm, where i do not need to loop over te array of structs to use te structfind to get the value from it.
right now looking at it,
Coldfusion - How to loop through an Array of Structure and print out dynamically all KEY values?
where i can loop over and get the value of the key match
but trying to check if something like this can be done
<cfset myvalue = structfindvaluefromAnything(myarrayofstruct,"infor")>
I like Sev's approach. I would change it slightly
<cfscript>
superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Spider-Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
avengers = superheroes.filter(function(item) {
return item.member == "Avengers";
});
writeDump(avengers);
</cfscript>
If you really want to do it in one line then you could use ArrayFilter() in combination with StructFindValue().
Adapting from the Adobe docs for ArrayFilter - https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-a-b/arrayfilter.html - something like this:
<cfscript>
superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
avengers=ArrayFilter(superheroes,function(item){
return ArrayLen(StructFindValue( item, "Avengers"));
});
writeDump(var=avengers, label="all matches");
writeDump(var=ArrayLen(avengers) ? avengers[1] : "Not found", label="first match only");
writeDump(var=structFindValue({"a":superheroes}, "Avengers", "all"), label="without arrayFilter");
</cfscript>
I believe the function available for this nearly exactly what you were hoping for...
StructFindValue(struct, value [, scope])
Searches recursively through a substructure of nested arrays, structures, and other elements for structures with values that match the search key in the value parameter.
Returns an array that contains structures keys whose values match the search key value. If none are found, returns an array of size 0.
Based on the gist you provided above (https://cffiddle.org/app/file?filepath=3e26c1ac-d5db-482f-9bb2-995e6cabe704/49b3e106-8db9-4411-a6d4-10deb3f8cb0e/24e44eba-45ef-4744-a6e6-53395c09a344.cfm), I think you've clarified your expectations a little bit.
In your gist, you say you want to be able to search an array of structs and find the row that has a "name" key with a value of "form". Then, you want to take the value of the "value" key that's associated with that struct in the array row. If there is no value then return 0.
You wanted to be able to do this in a single line of code, and the above answers do accomplish that. My answer essentially builds on those.
As demonstrated in the earlier answers, you still want to use closure functions to filter down your final output. Those are very quick and essentially built to do what you're trying to do.
The Fiddle that I worked with is here: https://cffiddle.org/app/file?filepath=b3507f1d-6ac2-4900-baed-fb3faf5a3b3a/e526afc2-bb85-4aea-ad0e-dcf38f52b642/75d88d2b-f990-44c1-9d9f-22931bf9d4d7.cfm
I've done two things with this.
First, I worked it as if you expected to encounter multiple records for your filtering value, and then turn those into a comma-delimited list. If you need another structure, the reduce() function in my code can be modified to handle this.
Second, I worked it as if you expected to encounter only one filtered record, returning only a single value.
The first thing I did, which is mostly the same in both methods, and which is essentially the same as the previous answers, is to filter your original array for just the value you want.
This is done like this:
myResult = originalArray.filter(
function(itm){
return itm?.name=="form"; /// ?. = safe-navigation operator.
}
)
I've broken it to multiple lines for clarity.
This will return a new array of structs consisting of your filtered rows.
But then you want to take those records and return the "value" from those rows (defaulting to 0 if no value. You can do this with a reduce().
commaDelimitedValue =
myResult.reduce(
function(prev,nxt) {
return prev.listappend( ( nxt.value.len() ? nxt.value : 0 ) ) ;
}
, "" /// Initialization value
) ;
Again, this can be written in one row, but I've included line breaks for clarity.
The reduce() function essentially just reduces your input to a single value. It follows the format of .reduce( function( previousValue, nextValue ){ return .... },<initializationValue>), where, on the first iterations, the initializationValue is substituted for previousValue, then previousValue becomes the result of that iteration. nextValue is actually the current iteration that you will derive a result from.
More at: https://coldfusion.adobe.com/2017/10/map-reduce-and-filter-functions-in-coldfusion/
In my assumption here, you could possibly have multiple rows returned from your filter(). You take those rows and append the value to a commma-delimited list. So you would end up with a result like 20,10,0,0 - representing 4 rows in your filtered results.
I also check for a length of the value and default it to 0 if it's an empty string. Above, I said that you could just use an Elvis Operator (:?) on that, but that doesn't work for a simple value like an empty string. Elvis works with NULLs, which the earlier array did have.
To put this back to one line, you can chain both of these functions. So you end up with:
myFinalResult =
myOriginalArray.filter(
function(itm){
return itm?.name=="form";
}
)
.reduce(
function(prev,nxt) {
return prev.listappend( ( nxt.value.trim().len() ? nxt.value : 0 ) ) ;
}
, ""
)
;
Again, that code is doing a lot, but it is still essentially one line. The final result from that would again be something like "20,10,0,0" for 4 rows with 2 defaulted to 0.
If you only expect your filter to return a single row, or if you only want a single value, you can simplify that a little bit.
myFinalResult = myOriginalArray.filter( function(itm){ return itm?.name=="fm" && (itm?.value.trim().len()>0) ; } )[1]["value"] ?: 0 ;
With this, I am back to using my previous trick with Elvis to default a row with no value, since I am filtering out the "form" struct with an empty-string "value". && is the same as AND. Technically this CAN filter more than one row from the original array, but the [1] will only pick the first row from the filtered rows. It also doesn't need to use a reduce(). If there's more than one row filtered, each iteration will just overwrite the previous one.
This will return a simple, single value with something like 42 - which is the last filtered value in the array, since it overwrites the previous row's value.
My Fiddle (https://cffiddle.org/app/file?filepath=b3507f1d-6ac2-4900-baed-fb3faf5a3b3a/e526afc2-bb85-4aea-ad0e-dcf38f52b642/75d88d2b-f990-44c1-9d9f-22931bf9d4d7.cfm) has some additional comments, and I set up a couple of edge cases that demonstrate the filtering and safe-navigation.
I would also like to reiterate that if this is Lucee 5+ or ACF2018+, you can shorten this further with Arrow Functions.

excel formulas sum over array

I have no clue why summing over the array formula doesn't return correct results. The formula is:
=SUM(INDEX($C$4:$C$12,TRANSPOSE(OFFSET(K17,0,0,1,COUNT(K17:S17)))))
What I want to do is select multiple values from $C$4:$C$12 based on values in K17:S17 and then sum it. In row K17:S17 there might 1 to 9 positive integers (from 1 to 9), that determine which row to select from C4:C12.
When I use just INDEX($C$4:$C$12,TRANSPOSE(OFFSET(K17,0,0,1,COUNT(K17:S17)))) as an array function it returns the values I want to be sumed properly. but adding sum in frot returns just the first value from this set.
In order to pass an array as INDEX's row_num or col_num parameter, a little coercion is required:
https://excelxor.com/2014/09/05/index-returning-an-array-of-values/
Also, it's better to avoid volatile OFFSET constructions if possible.
=SUM(INDEX($C$4:$C$12,N(IF({1},K17:INDEX(K17:S17,MATCH(9.9E+307,K17:S17))))))
which, incidentally, does not require CSE.
Edit: if the maximum value in the range K17:S17 is 9, we can replace 9.9E+307 with 10:
=SUM(INDEX($C$4:$C$12,N(IF({1},K17:INDEX(K17:S17,MATCH(10,K17:S17))))))
Regards

Excel nested if array

I am doing a nested min if array, and am having issue with it reading blanks.
=MIN(IF(Sheet1!$C:$C<=A24,IF(Sheet1!$AE:$AE>A24,Sheet1!$C:$C),IF(Sheet1!$C:$C<=A24,IF(Sheet1!$AE:$AE="",Sheet1!$C:$C))))
So in English, I'm asking that if the dates in sheet 1 column C are less than or equal to the value in A24, and the date in sheet 1 column AE is after the date in A24, OR the value in sheet 1 column AE is blank, give me the earliest date of what's left from column C. I hope that makes sense!
Any help would be greatly appreciated as I have spent literally hours on this trying isblanks, further nested if, all with no joy.
If you have Excel 2010 this AGGREGATE() Function will work:
=AGGREGATE(15,6,(Sheet1!C:C/((Sheet1!C:C<=A24)*((Sheet1!AE:AE>A24)+(Sheet1!AE:AE="")))),1)
If you have 2007 or earlier then the array formula should do it:
=MIN(IF(((Sheet1!C:C<=A24)*((Sheet1!AE:AE>A24)+(Sheet1!AE:AE=""))),Sheet1!C:C))
Being and array it must be entered with Ctrl-Shift-Enter instead of just Enter or Tab.
The issue is when using the or in arrays one should use +. So now if (Sheet1!AE:AE>A24) or (Sheet1!AE:AE="") are true it will return a 1 because 0+1=1.
Where the and portion is * because 0 * 1= 0. So both would need to be true to return the 1, or true.
Do you have to use nested if's?
Why not have the if statement evaluate all three conditions. An OR function to check two conditions (check for blanks OR value in col AE is greater an A24. Then, have the result of the OR function feed into an AND function along with the third check (column C less than or equal to A24). Then if the entire logical check of the IF function returns TRUE, use a MIN function to give you the minimum value in Column C. Likewise, if the logical check of the IF function evaluates to False,
do something else (ex. "No Match")
something like this:
=(IF(AND(COL_C <= $A$24, OR(COL_AE > $A$24, ISBLANK(COL_AE))),MIN(COL_C,"No Match"))

Excel: Array formula returning unexpected result

I have the formula:
=IF(C2=$C$1:C2,1,0)
which returns the value 1 (I believe this is correct as C2 will find a match with C2). My problem is that if I turn this into an array formula
{=IF(C2=$C$1:C2,1,0)}
this returns a value of 0, however I am not sure why.
Could someone provide an explanation on the different results from each formula?
In the non-array version, the fact that you are passing an array of cells to the IF function is redundant, since, without array-coercion, the array resolves to just the first cell within that array, i.e. C1, so effectively the (non-array) construction:
=IF(C2=$C$1:C2,1,0)
is identical to simply:
=IF(C2=$C$1,1,0)
In the array version, a 1 will only be returned if the values in both C1 and C2 are equal to the value in C1 (though of course this is a little redundant in itself, since the value in C1 will always be equal to itself!).
What exactly are you trying to do with this odd-looking construction?
Regards

Sum along absolute values in an Array in Matlab

My array contains a string in the first row
how can I sum the array from the 2nd row to the Nth/1442th row (as in my example) disregarding the negative signs present in the column?
for example, my code for an array called data2 is:
S = sum(data2(2,15):data2(1442,15));
so sum all of the elements from row 2 to row 1442 in column 15.
This doesn't work but it also does not have anything to deal with the absolute value of whatever row its checking
data is from a .csv:
You should do something like this:
sum(abs(data(2:1442,15)));
The abs function will find the absolute value of each value in the array (i.e. disregard the negative sign). data(2:1442,15) will grab rows 2-1442 of the 15th column, as you wanted.
EDIT: apparently data is a cell array, so you could do the following, I think:
sum(abs([data{2:1442,15}]));
Ok so it looks like you have a constant column so
data2(2,15) = -0.02
and further down
data2(1442,15) = -0.02 %(I would assume)
So when you form:
data2(2,15):data2(1442,15)
this is essential like trying to create an array but of a single value since:
-0.02:-0.02
ans =
-0.0200
which of course gives:
>> sum(-0.02:-0.02)
ans =
-0.0200
What you want should be more like:
sum(data2(2:1442,15))
That way, the index: 2:1442, forms a vector of all the row references for you.
To disregard the negative values:
your answer = sum(abs(data2(2:1442,15)))
EDIT: For a cell array this works:
sum(abs(cell2mat(data2(2:1442,15))))

Resources