openpyxl - get formula and value at the same time? - openpyxl

Is there any way to get both the cell formula and the cell value in the same load of the file in openpyxl?
Currently I have had to do the following:
wb_data_only = load_workbook(filename=file, data_only=True, read_only=True)
wb_formulas = load_workbook(filename=file, data_only=False, read_only=True)
And then I have to loop over the both these objects linearly in order to get the formula and value. For e.g. to get the data values only I do this:
for sheet in wb_data_only:
for row in sheet.iter_rows():
cell in row:
return cell.value
And then I have to repeat the same on the formula object.
Is there a more efficient way?

Related

How to copty only the value and not the formula into another workbook ? (openpyxl)

I will not put all my script, for the sake of clarity.
I created a workbook, where I stored data based on an csv files, then I created cells who contains sums of those data :
import openpyxl
wb = openpyxl.Workbook()
sheet = wb.active
sheet2=wb.create_sheet('somme')
sheet['C16'] = '= SUM(A100:A212)'
sheet['D16'] = '= SUM(B100:B212)'
sheet['E16'] = '= SUM(C100:C212)'
sheet['F16'] = '= SUM(D100:D212)'
But I want the sums to be in another sheet, but not the formula, just the value, because otherwise it will be 0 because in sheet2 there is no data to sum.
I tried this :
for row in sheet['A1':'Z100']:
for cell in row:
sheet2[cell.coordinate].value = cell.value
But it return formula, so I don't have the number, just 0.
Any ideas how to fix that ?
You could do this by having the cells in sheet2 reference the sum cells with a formula, like =Sheet1!C16.
If you already know the title of sheet, you can just use that directly. If not, it's probably a good idea to use openpyxl.utils.cell.quote_sheetname() to avoid potential issues with spaces:
sheet2[cell.coordinate].value = f"={quote_sheetname(sheet.title)}!{cell.coordinate}"
Another approach, if you don't want the sums in the main worksheet, would be to put the sum formulas directly in sheet2, and add the title of the first worksheet to the cell ranges.

Merging duplicate column data without losing the values in the rest of the rows in Excel

I'm trying to find a way to merge duplicate values in the first column of my data without losing the unique values in the rest of the rows.
e.g. at the moment my data looks like this:
and I want it to look like this:
Actually, you need to focus only on the first column. The second has nothing to do with it.
Here is some pseudo code, that would work, if you translate it to VBA:
FOR EACH CELL IN COLUMN NUMBER
IF CELL = OFFSET(PREVIOUS CELL) AND CELL.COLUMN > 1 THEN
CELL.TEXT = ""
END IF
NEXT CELL
FOR EACH CELL IN COLUMN NUMBER
IF CELL = "" THEN
ENLARGE THE RANGE
ELSE
MERGE THE RANGE
END IF
NEXT CELL
IF RANGE <> NOTHING THEN
MERGE THE RANGE
END IF

Return values from other workbook

Have a question about formula which will resolve my issue.
In my main workbook I need to compare data from two sources.
One of the columns must retrieve data(amounts) from other workbook.
I want formula which will search for all amounts in column G and will skip all blank cells. Tried to use VLOOKUP, INDEX and SMALL functions but no effect.
Each day amounts are different and I need to match them in main file and find exeptions.
Any ideas?
How about an array formula such as the following?
=INDEX($G$2:$G$20,SMALL(IF(($G$2:$G$20)=0,"",ROW($G$2:$G$20)),ROW()-1)-ROW($G$2:$G$20)+1)
The formula would have to be placed into cell I2 as an array formula (which must be entered pressing Strg + Shift + Enter). Then you can drag down the formula to get all the other values.
It doesn't have to be in column I but it has to be in row 2 because this formula get's the n-th Number from the list which is not = 0. The n-th place is (in this formula) row()-1. So for row 2 it will be 2-1=1 and thus the 1st number. By dragging down the formula you get the 2nd, 3rd, etc. number. If you start with the formula in cell I5 instead then it would have to be adjusted to be as follows:
=INDEX($G$2:$G$20,SMALL(IF(($G$2:$G$20)=0,"",ROW($G$2:$G$20)),ROW()-4)-ROW($G$2:$G$20)+1)
You could loop through the column and store each value >0 in an array and then compare or you loop through the column and compare directly...
something like:
Dim i as Integer = 0
Foreach value in Maintable
Do
If otherworkbook.cells(i,7) = value Then '7 for G
do your stuff
End If
i = i + 1
While i < otherworkbook.rows.count
Next
I think that could be the right approach

Excel: Check if cell string value exists in column, and get all cell references to that string

I suspect this may be a job for VBA, which is beyond my abilities. But here's the scenario:
Column A in Sheet 1 (CAS1) contains x rows of text values
Column A in Sheet 2 (CAS2) contains x rows of text values
Part A - For each row value in CAS1, I need to know if the string is contained in any of the cells in CAS2. Not exact match, the string can be only part of the searched cells.
Part B - I need to know the cell value of each cell in CAS2 that contains the CAS1 value (if they do exist, they can be listed in the cells adjacent to the cell being searched in CAS1).
I've tried the following to attempt Part A, all to no avail:
vlookup(A1,sheet2!A:A,1,false)
NOT(ISNA(MATCH(A1,sheet2!A:A,0)))
ISNUMBER(MATCH(A1,sheet2!A:A,0))
COUNTIF(sheet2!A:A,A1)>0
IF(ISERROR(MATCH(A1,sheet2!A:A, 0)), "No Match", "Match")
I know some of the cell values in CAS2 contain the cell values in CAS1, so I don't know why they return false or No Match. I suspect it may be down to the nature of the text content. So here's some sample data:
CAS1
LQ056
RV007H
RV008
RV009H
TSN304
TSN305
CAS2
RV009-satin-nickel-CO.jpg
STR314.jpg
STR315.jpg
HCY001.jpg
RV008-oval-rad-CO.jpg
HCY001-BRAC006.jpg
Any help would be appreciated.
This problem can be faced through VBA (at least, I imagine the VBA solution much more easily than the possible Excel one). You need a macro that, for each row in CAS1, search the content in each row of CAS2 and returns you the address.
For Each cell In Sheets("CAS1").Range("A1:A" & Sheets("CAS1").Range("A1").End(xlDown).Row) '<-- check each cell of the range A1:A? of sheet CAS1 (adapt "A" and "1" if they're different)
recFound = 0 '<-- count how many findings there are
For Each cell2 In Sheets("CAS2").Range("A1:A" & Sheets("CAS2").Range("A1").End(xlDown).Row) '<-- check in each cell of the range A1:A? of sheet CAS2 (adapt "A" and "1" if they're different)
If InStr(cell2.Value, cell.Value) <> 0 Then '<-- if the value in cell is contained in the value in cell2..
recFound = recFound + 1 '<-- account the new finding
cell.Offset(0, recFound) = Split(cell2.Address, "$")(1) & Split(cell2.Address, "$")(2) '<--write the address on the right of the currently searched cell
End If
Next cell2
Next cell
All the above should be enclosed in a macro, e.g. Sub makeMySearch(), that should be run to get the results. As commented in my code, I'm assuming that data are in A1:A? of both sheets; but they of course might be, for example, in B5:B? of the sheet 1 and in C7:C? of the sheet 2. You need clearly to adapt the code to your current data.
There's no need for VBA. Some simple array-formulas can do the job.
To see if the entry in CAS1 is present in CAS2:
=OR(ISNUMBER(SEARCH(A2,CAS2_)))
will return TRUE or FALSE. BUT this formula has to be entered by holding down CTRL-SHIFT while hitting ENTER If you do this correctly, Excel will place braces {...} around the formula that you can see in the formula bar.
The SEARCH function returns an array of results, which will be either the #VALUE! error, or a number.
In order to return the address, the following array-formula can be entered adjacent to a cell in CAS1:
=IFERROR(ADDRESS(LARGE(ISNUMBER(SEARCH($A2,CAS2_))*ROW(CAS2_),COLUMNS($A:A)),1),"")
Fill right for the maximum number of addresses possible, then select the group and fill down.
In this case, the array being returned is a string of either 0's, or 1 * the row number (i.e. the row number). I assumend the data in CAS2 was in column A, but you can change the column number if needed (or even compute it if necessary, by replacing the 1 in the ADDRESS function with COLUMN(CAS2_))
CAS1_ and CAS2_ are either named ranges, or absolute range references to the two text groups.

Vba Excel How to edit cell values in particular columns without looping all Rows based on Cell Contents?

I have a SpreadSheet with data like shown. I want to hide the value of the cell in last column of a row if the corresponding SOA column in the same row has a value of 1A. I have some thousands of rows so i don't want to loop through all rows. Is there any alternative instead of looping through all rows? Any help would be appreciated greatly.
You do not need VBA for this. Insert the following formula into cell F2 and fill down:
=IF(C2="1A","",E2)
You could also do this using Format as Table, filtering SOA by the value 1A and then deleting the contents of the Exclusive row.
Finally, if you must use VBA, use program logic like the following pseudo-code:
For each cell in SOA
If cell.value = "1A"
Range("E" & cell.Row).Value = ""
Next cell