VBA (Excel) 2010: specific script how-to, dealing with range comparisons - arrays

I have 3 columns that follow these rules:
Any cell in Col A may be empty, otherwise it contains a string.
If a row in Col A is empty, that row in Col's B and C will be empty.
If a row in Col A is populated, That row in Col B will be populated with an integer, and that row in Col C may be empty or have a "1".
I need a script that, for each cell in Col A,
Checks the string in that cell and looks for all identical strings in Col A.
For each row containing said matching string, checks Col C for a "1"
For each row where both of the above are true, sum the values in Col B and replace each of them with that sum.
So for example, this:
A B C
x 1
x 2
z 2 1
y 1 1
y 2 1
y 1
z 2 1
z 1 1
Should become this:
A B C
x 1
x 2
z 5 1
y 3 1
y 3 1
y 1
z 5 1
z 5 1
So 3 different strings are found in Col A, (x y and z). Duplicates of x are found but the values in col B are not summed because there was no "1" in Col C. Dupes of y are found, but only those with a "1" to the right are summed. All dupes of z found have a "1", so all are summed.
What's the best way to do this? Please let me know if I need to clarify something about this question (I know it's convoluted, but I spent a lot of time trying to make it as clear as possible haha).

A simple formula should do the job : assuming the data are found in A:C
=IF(ISBLANK(A2),"",IF(ISBLANK(C2),B2, SUMIFS(B:B,A:A,A2,C:C,1)))
The outer IF display nothing when column A is empty.
The inner IF displaycolumn B when column C is empty.
The SUMIFS will add upcolumn B where column A is the same(as current row) and when column C is 1.

Related

How to include OR in Array formula where ONLY SOME criteria need to be TRUE - Excel Part 2

This is an extension of this question, which was answered by XOR LX.
In the original question, I wanted to count the number of rows in a matrix that included values that fulfilled at least one column-specific criteria. The columns referenced were sequential (e.g., A1:D4), but the situation requires indexing columns that are not sequential.
Take the data matrix:
A B C D E F G H
4 2 2 2 1 4 2 4
5 2 1 3 4 1
3 2 1 4 5 1
1 2 3 5 3 2 2 2
2 2 2 2 2 2 2 2
With the column-specific criteria:
Column A criteria: >2
Column B criteria: >2
Column C criteria: <2
Column D criteria: >4
Column E criteria: >2
Column F criteria: >3
Column G criteria: >2
Column H criteria: >3
In order to count the number of rows that have values for all columns, as well as meet at least column-specific criterion, we could use XOR LX's code:
=SUM(N(MMULT(IF(MMULT(N(A1:H5=""),TRANSPOSE(COLUMN(A1:H5)^0))=0,COUNTIF(OFFSET(A1,ROW(A1:H5)-MIN(ROW(A1:H5)),COLUMN(A1:H5)-MIN(COLUMN(A1:H5))),{">2",">2","<2",">4",">2",">3",">2",">3"}),0),TRANSPOSE(COLUMN(A1:H5)^0))>0))
...which might have some problems with handling blanks, so the alternative:
=ROWS(A1:D4)-COUNTIFS(A1:A5,"<=2",B1:B5,"<=2",C1:C5,">=2",D1:D5,"<=4",E1:E5,">=2",F1:F5,">=3",G1:G5,">=2",H1:H5,">=3")-COUNT(1/N(MMULT(N(A1:H5=""),TRANSPOSE(COLUMN(A1:H5)^0))>0))
...which as far as I have tested seems to handle blanks regardless of their frequency or location.
But what if only rows with data for certain should be counted, ignoring the other columns. Furthermore, what if only certain columns should be evaluated for their criteria?
Let's take the example above. Our goal is to count the number of rows with i) full data in certain columns and ii) entries in certain columns that meet at least one column-specific criterion.
The relevant columns that should have full data are A:C and F:H. The columns that should be evaluated for their criteria are A:C only. The solution is 2 since:
There are 5 rows in total, but only rows 1, 3, 4, and 5 qualify as they have full data in columns A:C and F:H. Row 2 has missing data in column A and also G, either of which should disqualify its inclusion. Notice that even though row 3 has missing data in columns D and E, it is still included because these columns are not important.
Rows 1 and 3 meet the specified criteria, since i) they have full data in columns A:C and F:H, and ii) at least one entry meets one of the criteria for columns A, B, and C; specifically, the value in row 1, column A (4 > 2), and row 3, column A (3 > 2) and column C (1 < 2). Notice that this is still only counted as one row meeting the criteria, even though the criteria for 2 columns is met.
Your thoughts would be most welcome. Please also show some love to XOR LX in the original post - I am sure you will find his solution helpful.
#PyjamaNinja has encouraged me to try and develop my 'lemma' to their earlier question.
The method is:
A Count rows with entries in all specified columns (A-C and F-H)
B Count rows with entries in all specified columns that do _not_ meet any of the criteria in the columns being evaluated (A-C).
Subtract B from A.
=COUNTIFS(A1:A5,"<>",B1:B5,"<>",C1:C5,"<>",F1:F5,"<>",G1:G5,"<>",H1:H5,"<>")-COUNTIFS(A1:A5,"<=2",B1:B5,"<=2",C1:C5,">=2",F1:F5,"<>",G1:G5,"<>",H1:H5,"<>")
With many thanks to #XOR LX.

Excel find equal values and multiply another cell

I have a sheet the the followin info
"A" "B"
_____
a 1
a 1
a 1
a 1
a 1
a 1
b 1
b 1
b 0
b 1
b 1
b 1
c 1
c 0
c 1
d 1
d 1
I Like to have an Array formula that multiplies al values on column "B" that have the same text on column "A"
I don't whant to use a pivot table, i need to solve this with formulas.
End result should be on column "C"
a = 1
b = 0
c = 0
d = 1
thanks
I can do it in two columns....
Column C formula
=IF(A3=A2,C3*B2,B2)
Column D formula
=IF(A2<>A1,CONCATENATE(A2," = ",C2),"")
gives results like this
Excel image
if the blanks are a problem,
http://www.cpearson.com/excel/NoBlanks.aspx
DPRODUCT() might do it, as well, if you can setup the criteria correctly.
OK i solved using this formula as array
=+SI(SUMA(SI((A:A=A1);B:B;0))=CONTAR.SI(A:A;A1);"OK";"")

How I can aply a filter condition to two colums in a table using M language?

In Power BI I have a table with 3 columns as shown below, And I need to remove only the row that apply both (Have the "customer" value repeat) && (have the "Month" value repeat). In order to do that, I need to find an M function because the Power BI interface does not have a function defined, so if there is a way to do it, must be on "Advanced Editor which use "M" language.
Table:
I have:
Customer Month Bill
A 1 x
A 1 x
B 1 x
B 2 x
C 1 x
I want:
Customer Month Bill
A 1 x
B 1 x
B 2 x
C 1 x

How to check if all the entries in columns of a matrix are equal (in MATLAB)?

I have a matrix of growing length for example a 4-by-x matrix A where x is increasing in a loop. I want to find the smallest column c where all columns before that, each, carry one single number. The matrix A can look like:
A = [1 2 3 4;
1 2 3 5;
1 2 3 1;
1 2 3 0];
where c=3, and x=4.
At each iteration of the loop where A grows in length, the value of index c grows as well. Therefore, at each iteration, I want to update the value of c. How efficiently can I code this in Matlab?
Let's say you had the matrix A and you wanted to check a particular column iito see if all its elements are the same. The code would be:
all(A(:, ii)==A(1, ii)) % checks if all elements in column are same as first element
Also, keep in mind that once the condition is broken, x cannot be updated anymore. Therefore, your code should be:
x = 0;
while true
%% expand A by one column
if ~all(A(:, x)==A(1, x)) % true if all elements in column are not the same as first element
break;
end
x = x+1;
end
You could use this:
c = find(arrayfun(#(ind)all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
This finds the first column where not all values are the same. If you run this in a loop, you can detect when entries in a column start to differ:
for x = 1:maxX
% grow A
c = find(arrayfun(#(ind)~all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
% If c is empty, all columns have values equal to first row.
% Otherwise, we have to subtract 1 to get the number of columns with equal values
if isempty(c)
c = x;
else
c = c - 1;
end
end
Let me give a try as well:
% Find the columns which's elements are same and sum the logical array up
c = sum(A(1,:) == power(prod(A,1), 1/size(A,1)))
d=size(A,2)
To find the last column such that each column up to that one consists of equal values:
c = find(any(diff(A,1,1),1),1)-1;
or
c = find(any(bsxfun(#ne, A, A(1,:)),1),1)-1;
For example:
>> A = [1 2 3 4 5 6;
1 2 3 5 5 7;
1 2 3 1 5 0;
1 2 3 0 5 8];
>> c = find(any(diff(A,1,1),1),1)-1
c =
3
You can try this (easy and fast):
Equal_test = A(1,:)==A(2,:)& A(2,:)==A(3,:)&A(3,:)==A(4,:);
c=find(Equal_test==false,1,'first')-1;
You can also check the result of find if you want.

Selecting certain zero elements from a matrix in MATLAB

Suppose I have this matrix:
M =
90 0 40 0
0 0 10 60
55 15 0 10
0 15 5 0
I would like to find all zeroes such that once I select a zero from row i and column j, no more zeroes from row i and column j should be selected. In this example, scanning from left to right and top to bottom, the rows and columns I should get are:
Row Column
1 2
2 1
3 3
4 4
What MATLAB code will produce this for me?
Luis Mendo's answer is correct given the OP's original question of selecting the first zero of each row. However, with our discussion seen in the comments, this requirement has now changed. The understanding (at least from what I gathered from the snippets of code and the requirements) is that once you select a zero at row i and column j, no more zeroes from all of row i and all of column j should be selected. As for loops are usually frowned upon in MATLAB, I saw no other choice but to do this with loops.
My approach was the following:
Find all zeroes in the matrix and find their row and column locations and place each into separate lists.
While we still have a zero to consider:
Place this row and column into a list
Remove all indices from the row list that contain this row in question.
Remove all indices from the column list that contain this column in question
Repeat Step #2 until our row and column location list is empty.
Without further ado, here is the code.
clear all;
close all;
M =[90,0,40,0;0,0,10,60;55,15,0,10;0,15,5,0];
%// Find row and column locations
%// find traverses columns first, so I had to
%// transpose and swap J,I so that it reports
%// row locations first.
[J,I] = find(M.' == 0);
%// List of co-ordinates that meet our criteria
rowCoords = [];
%// While there is still one zero to consider...
while (~isempty(I) && ~isempty(J))
%// Store these for processing
rowToAdd = I(1);
colToAdd = J(1);
%// Add to the list
rowCoords = [rowCoords; [rowToAdd colToAdd]];
%// Remove all row and column co-ordinates
%// that share the same row and column
rowsToRemove = I == rowToAdd | J == colToAdd;
I(rowsToRemove) = [];
J(rowsToRemove) = [];
end
As such, we finally get:
rowCoords =
1 2
2 1
3 3
4 4
It is still unclear on whether the OP wants what Luis Mendo has provided, or what I have interpreted in the comments. The OP verified that how I specified his requirements is what he actually wants but we still do not have any verification. OP: Please verify.
Find the first zero (if any) in each row
[ind1, ind2] = max(M.'==0);
result = [find(ind1); ind2(ind1)].';
In your example, this gives
result =
1 2
2 1
3 3
4 1
Find the first zero (if any) in each row, ignoring columns that have been previously used
M2 = M; %// make a copy of M2. It will be overwritten
[R C] = size(M2);
cols = NaN(R,1); %// initiallize. This will store the result for each row
ind = false(R,1); %// intiallize. This will indicate rows that have a valid zero
for r = 1:R %// for each row
c = find(M2(r,:)==0,1); %// find first valid zero, if any
if ~isempty(c)
cols(r) = c; %// store result
ind(r) = true; %// this row has been found to have a valid zero
M2(:,c) = inf; %// this col can no longer be used
end
end
result = [find(ind) cols(ind)]; %// build result. Only rows with a valid zero
In your example:
result =
1 2
2 1
3 3
4 4

Resources