I have a spreadsheet with a load of random text and numbers in column A like so:
Column A
Row 1 = 471806121601 5205569 - 0007 Standard White Toilet Tissue 27
Row 2 = 471814121601 5206177 - 0014 Premium White Toilet Tissue 6
Row 3 = 471814121601 5206178 - 0007 Premium White Toilet Tissue 27
Row 4 = 471806121601 5206180 - 0014 Premium Kitchen Towel 2x75l 6
I have about 2000 lines in total. In each cell, is a Purchase order number (12 digits) and an item number next to it (7 digits).
I am trying to extract the po number and put it into column B and extract the item number and put it into column C
Column B Column C
471806121601 5205569
471814121601 5206177
471814121601 5206178
471806121601 5206180
Here is my code:
Option Explicit
Sub main()
Dim cell As Range
Dim arr As Variant, arrElem As Variant
With Worksheets("Orders") '<--| change "Strings" to your actual worksheet name
For Each cell In .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
arr = Split(Replace(cell.Value, " ", " "), " ") '<--| change "A"'s to your actual relevant column index
For Each arrElem In arr
If IsNumeric(arrElem) Then
If Len(arrElem) = 12 Then cell.Offset(0, 1).Value = arrElem
End If
Next arrElem
Next cell
End With
Dim cell2 As Range
Dim arr2 As Variant, arrElem2 As Variant
With Worksheets("Orders") '<--| change "Strings" to your actual worksheet name
For Each cell2 In .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
arr2 = Split(Replace(cell2.Value, " ", " "), " ") '<--| change "A"'s to your actual relevant column index
For Each arrElem2 In arr2
If IsNumeric(arrElem2) Then
If Len(arrElem2) = 7 Then cell2.Offset(0, 3).Value = arrElem2
End If
Next arrElem2
Next cell2
End With
End Sub
This code does work. However it takes absolutely ages and only does one line at a time...Slowly.
Is there a quicker way of doing this? Thanks
If your PO and IN are always the same length in col B put
=MID(A2, 1, 12)
And in col C
=MID(A2, 14, 7)
However if your number change but are always the first two swap the above for,
=MID(A2,1,FIND(" ",A2,1)-1)
And
=MID(A2, FIND(" ", A2, 1)+1, 7)
Respectively.
just use split(string,delimiter)(0) and (1) why replace the space, just use that as the delim. If Row # is in, then use (1) and (2), or you could consider split(split(input,"-")," ") maybe a little faster, not sure though. Also, once you're done no need to complete the loop, so consider, do until with flags rather than for next, although exit for is available
Formula wise, it could be done using something like this
=MID(D1,FIND("é",SUBSTITUTE(D1," ","é",3)),FIND("é",SUBSTITUTE(D1," ","é",4))-FIND("é",SUBSTITUTE(D1," ","é",3)))
and
=MID(D1,FIND("é",SUBSTITUTE(D1," ","é",4)),FIND("é",SUBSTITUTE(D1," ","é",5))-FIND("é",SUBSTITUTE(D1," ","é",4)))
Related
EDIT, in short:
I have an excel table that looks like this:
tabel
Each cell contains a range of numbers.
I'm looking for a function that can search for a number in the whole table and indicate me the column and row where the range that it belongs to is located.
So, if I search for number 2346, it should function like this:
function (2346) > result (C1, R2)
I have a huge archive of photos (about 300.000 items) in a library.
Photos are stored in boxes, the boxes in shelves.
Each box has a range of inventory numbers of photos.
I want to create a map of the deposit that looks like this:
Shelf 1 - contains boxes 1, 2, 3, etc.
Box 1 - contains photos with inventory numbers between 1257-1321
Box 2 -"- between 2345-2522
Box 3 -"- between 123523-123643
Translated in an excel table, it would look like this:
Column 1 would be Shelf 1, containing the boxes with the images:
cell in column 1 / row 1 (that's box 1) contains the range of numbers: 1257-1321
cell in column 1 / row 2 (box 2), the range: 2345-2522
cell in column 1 / row 3 (box 3), range: 123523-123643
They are not in order, because they entered in more than 100 years in the collection, and they are arranged as they entered and by subject. Not to mention that the deposit has been moved a few times. So, I have a hard time to find one image when I'm looking for it.
But if I have this excel table, this map of the deposit, supposedly I want to enter the inventory number that I'm looking for, let's say "2346", and a formula that search throughout the whole table would indicate me that the item (number I look for, is in a range that is located in column 1, row 2, that means shelf one, box 2 in the deposit).
Actually the concept is very simple, excel is able to do MUCH more difficult tasks, and I'm amazed I can't find a way to do this. I'm a photographer and librarian, so my experience in programming is close to zero.
Thank you very much if you can help!
It's a bit of a lenghty one but not hard to understand, I made some sample data like so:
="C"&SUMPRODUCT(((VALUE(LEFT($A$1:$B$3,SEARCH("-",$A$1:$B$3)-1))<=D3)*(VALUE(RIGHT($A$1:$B$3,LEN($A$1:$B$3)-SEARCH("-",$A$1:$B$3)))>=D3))*COLUMN($A$1:$B$3))&", "&"R"&SUMPRODUCT(((VALUE(LEFT($A$1:$B$3,SEARCH("-",$A$1:$B$3)-1))<=D3)*(VALUE(RIGHT($A$1:$B$3,LEN($A$1:$B$3)-SEARCH("-",$A$1:$B$3)))>=D3))*ROW($A$1:$B$3))
Youll just have to change ranges
If you set up your worksheet like the image below (sheet name "Library"):
You could try the code:
Option Explicit
Sub Painting()
Dim LastRow As Long, i As Long, SearchValue As Long
Dim arrData As Variant
'Let s say that all data appears in sheet called Library
With ThisWorkbook.Worksheets("Library")
'Let s say that we are looking for the value in cell H1
SearchValue = .Range("H1").Value
'Find the Last row of column A sheet Library
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
'Create as array from Column A row 2 up to Column D row LastRow
arrData = .Range(.Cells(2, 1), .Cells(LastRow, 4)).Value
'Loop Array to find a match
For i = LBound(arrData) To UBound(arrData)
If arrData(i, 3) <= SearchValue And arrData(i, 4) >= SearchValue Then
'Pop up with Shelf & Box name
MsgBox "Search Value " & SearchValue & " included in:" & vbNewLine & "Shelf: " & arrData(i, 1) & vbNewLine & "Box: " & arrData(i, 2)
'Select the line where the Search Value found
.Range("A" & i + 1 & ":D" & i + 1).Select
'Exit the loop
Exit For
End If
Next i
End With
End Sub
Result:
I just wonder how to calculate this in vba:
Calculate the first amount of money if it is yes but not calculate the amount if it is no. Imagine there are four cells:
(cell 1) abcbc bcbcbcb cbcbcbc $1000/kskskksks/$2000//1222/1221/11/yes
(cell 2) any words will be here $2300/heyhey hey/ //3232//3232/no
(cell 3) kakjsak dsdsk kdjskj 2323/ $23232/hhehe 22/33/333/yes
(cell 4) kakaka kjsdkj ksjskjds kdjsjkdj 11 223 222/ $1121/ $2121/yes
The algorithm is to check whether is yes or no. Then, on each line, find the first money, beginning with $, the second money on the same line would not take into account.
In this example, the program will take $1000 into account, because it is yes, second line would not be executed since it is no. And the third cell would take the first money (first $), $23232. So, the program will sum $1000+$23232+$1121=$25353
I guess that this is what you want, considering that you are using the first column to place each value and your sheet's name is "Sheet1"
Sub SumFirstAmountIfYes()
Dim AmountSum As Variant ' Declares the AmountSum
lastRow = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row ' Finds the last used row on the first column
For i = 1 To lastRow ' Iterates over the rows to the last row
StringValue = Sheets("Sheet1").Cells(i, 1).Value2 ' Gets the value to a variable
If StringValue Like "*yes" Then ' Checks if the string terminates with "yes"
FirstDollar = InStr(StringValue, "$") ' Finds first dollar symbol "$"
FirstSlashAfterDollar = InStr(FirstDollar, StringValue, "/", 0) ' Finds first slash "\" after the first dollar symbol
FirstAmount = Mid(StringValue, FirstDollar + 1, FirstSlashAfterDollar - FirstDollar - 1) ' Gets the amount of each row
AmountSum = AmountSum + CDec(FirstAmount) ' Adds to the sum variable each found amount
End If
Next
MsgBox (AmountSum) ' Shows the final sum of the amounts
End Sub
This uses split to isolate yes/no and InStr to locate the first currency symbol.
Sub sumYes()
Dim i As Long, str As String, dbl As Double
With Worksheets("Sheet10")
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
str = LCase(.Cells(i, "A").Value2)
If Split(str, "/")(UBound(Split(str, "/"))) = "yes" Then
If CBool(InStr(1, str, Chr(36))) Then
dbl = dbl + Val(Mid(str, InStr(1, str, Chr(36)) + 1))
End If
End If
Next i
.Cells(2, "B") = dbl
End With
End Sub
Slightly different approach which uses excel array formula assuming your cell values doesn't contain trailing spaces ,
=SUM(IF(RIGHT(A1:A4,1)="s",MID(A1:A4,SEARCH("$",A1:A4)+1,SEARCH("/",A1:A4,SEARCH("$",A1:A4))-SEARCH("$",A1:A4)-1)*1,""))
I have a spreadsheet in which there are multiple rows that have three columns (K, L M) that contain text (inserted manually from a dropdown). The inserted text includes a 'score'. For the row shown in the image that score is 3 + 2 + 2 = 7.
What I'd like to be able to do is to have that score automatically calculated and shown in column N. I'm happy to do the score extraction given the text, but I'm completey unfamiliar with Excel's object model, and how to write a VBA macro that can be triggered across all of the rows. I assume it would be passed a range somehow, or a string designating a range, but how to do that is beyond me just now. Perhaps I just need a formula? But one that calls a function to strip non-numerical data from the cell?
Any help appreciated.
Put this formula in N2 cell and drag it all the way down.
=LEFT(K2, FIND("-", K2) - 2) + LEFT(L2, FIND("-", L2) - 2) + LEFT(M2, FIND("-", M2) - 2)
For more information see reference. It sum all numbers, that are present before the hyphen (-) in a cell.
Try:
N2 = LEFT(TRIM(K2),1) + LEFT(TRIM(L2),1) + LEFT(TRIM(M2),1)
As I said in comments, this solution does not scale so well if it is more than three columns and / or the scores are more than single digit [0-9]
A VBA solution to do all of your rows and enter the values into Column N:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row
'get the last row with data on Column A
For rownumber = 1 To LastRow 'loop through rows
For i = 11 To 13 'loop through columns
strValue = ws.Cells(rownumber, i).Value 'get text string from cell
pos = InStr(strValue, " -") 'find the dash - in cell
If pos > 0 Then 'if dash found
Value = Value + Val(Left(ws.Cells(rownumber, i).Value, pos - 1)) 'remove everything after number
End If
Next i
ws.Cells(rownumber, 14).Value = Value 'write value to column N
Value = 0
Next rownumber
End Sub
I have about 800 rows of data in a single column. The data in the column looks like this:
|Column A |
|195Marriott International127,500|
How can I have excel break this out into columns so it looks like as follows:
|Column A| |Column B| |Column C|
|195| |Marriott International| |127,500|
Thank you!
EDIT:
But you do have some patterns that you can use. Note that you have numbers in the first and third column items. Select column A, extract last character with =RIGHT(). You will only need 9 replacements to make (1 - 9), then concatenate the value. Do the same with the first number in the 3rd Column. You will then have delimiting values to use text to column.
====
you can use the Text to Column tool under the data tab on excel.
You can split the contents of one or more cells in a column, and then distribute those contents as individual parts across other cells in adjacent columns. For example, if your worksheet contains a column of full names, you can split that column into separate first name and last name columns.
I see you found a solution from Solar Mike's comment, but here's an alternative.
Below is a short piece of VBA that will preprocess the data, inserting a delimiter that will work. I tested it on a limited amount of data, so it may generate errors that need to be corrected.
The VBA ...
Option Explicit
Sub MakeDelimiters()
Dim LastRow As Long, iLoop As Long
Dim myDelim As String, myStr As String
Dim theRng As Range, theSht As Worksheet, theCell As Range
myDelim = ";"
Set theSht = Worksheets("Sheet1")
LastRow = theSht.Range("A" & theSht.Rows.Count).End(xlUp).Row
Set theRng = theSht.Range("A1:A" & LastRow)
For Each theCell In theRng
myStr = theCell.Value
For iLoop = 1 To Len(myStr)
If IsNumeric(Mid(myStr, iLoop, 1)) And _
Not IsNumeric(Mid(myStr, iLoop + 1, 1)) And _
Mid(myStr, iLoop + 1, 1) <> "," Then
myStr = Left(myStr, iLoop) & myDelim & Right(myStr, Len(myStr) - iLoop)
End If
If IsNumeric(Mid(myStr, iLoop + 1, 1)) And _
Not IsNumeric(Mid(myStr, iLoop, 1)) And _
Mid(myStr, iLoop, 1) <> "," And _
Mid(myStr, iLoop, 1) <> myDelim Then
myStr = Left(myStr, iLoop) & myDelim & Right(myStr, Len(myStr) - iLoop)
End If
Next iLoop
theCell.Value = myStr
Next theCell
End Sub
Converted this ...
to this ...
UPDATE:
The idea that a future visitor's data would look oversimplified ...
Is the lhs always 3 numbers, never 2 or 4? And the right 3,3? -SolarMike
... is laughable. So I came up with this solution. This answer is predicated on the comments:
Are columns A and C always going to be numeric? Or more precisely, will column A always end with a number, and will column C always start with a number? – OldUgly
#OldUgly - yes, that is the case here – BigMike
To answer your original question about "Text to Columns", I have no idea. However, this was tagged excel-formula, so I gave it a shot and came up with this ...
... which assumes you really want the data in columns A-C. The two helper array formulas (Ctrl+Shift+Enter)in E2 and F2 respectively are ...
=MIN(FIND(0,SUBSTITUTE(D2,CHAR(ROW(INDIRECT("65:90"))),0)&0))
=MIN(FIND(0,SUBSTITUTE(D2,CHAR(ROW(INDIRECT("48:57"))),0)&0,E2+1))
The formulas in A2, B2, and C2 respectively are (pretty obvious) ...
=LEFT(D2,E2-1)
=MID(D2,E2,F2-E2)
=RIGHT(D2,LEN(D2)-F2)
Ah, good old ASCII tables
I have to average sets of 3 columns.
EXAMPLE:
Blood_Patient1_0_R1, Blood_Patient1_0_R2, Blood_Patient1_0_R3
There average is in a new column Blood_Patient1_0
Similarly, Blood_Patient1_3_5_R1, Blood_Patient1_3_5_R2, Blood_Patient1_3_5_R3
The average is in a new column Blood_Patient1_3_5
This process is being repeated for 8 such sets of columns.
Currently I am averaging using the formula: IF(ISERROR(AVERAGE(B7:D7)),"",AVERAGE(B7:D7)) and auto-filling 21,000 plus rows.
Since there is a pattern in column headings, I was thinking to automate the whole process.
This is what I have thought so far in terms of algorithm:
0, 3_5, 6_25 are time values in column headers.
at each time instant, there are 3 replicates R1, R2,R3 as part of column headers
for time array [3.5h, 6.25h, 9.5h, 11.5h, 16.5h, 25h, 49h, and 156h
]
create a new column
for rows from 2 to 21458
average over replicates from R1 to R3 using above formula
I do not know how to write this in excel. Any help would be appreciated.
Give this a go.
This solution assumes that you have a continuous data set, that is, no gaps between the columns you wish to search through.
Firstly, you will need to include this function. Paste it into the same module as the subroutine. The purpose of this function is to allow the string in each heading to be compared against an array of substrings, as opposed to the single substring permitted by the InStr function.
Function Time_Search(strCheck As String, ParamArray anyOf()) As Boolean
Dim item As Long
For item = 0 To UBound(anyOf)
If InStr(1, strCheck, anyOf(item)) <> 0 Then
Time_Search = True
Exit Function
End If
Next
End Function
Next, paste in this subroutine. I have assummed that the dataset begins at cell A1. Also, I have allowed for a dynamic range, should the number of columns or rows ever change.
Sub Insert_Average_Columns()
Dim HeaderRange As Range
Dim LastRow As Long
Dim c As Range
Set HeaderRange = Range(Range("A1"), Range("A1").End(xlToRight))
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For Each c In HeaderRange.Cells
If Right(c.Value, 2) = "R3" Then
If Time_Search(c.Value, "3_5", "6_25", "9_5", "11_5", "16_5", "25", "49", "156") Then
c.Offset(0, 1).EntireColumn.Insert
c.Offset(0, 1) = "Average of " & Left(c.Value, Len(c.Value) - 3)
c.Offset(1, 1).FormulaR1C1 = "=IFERROR(AVERAGE(RC[-3]:RC[-1]),"""")"
c.Offset(1, 1).AutoFill Range(c.Offset(1, 1).Address, Cells(LastRow, c.Offset(1, 1).Column))
End If
End If
Next c
End Sub
There is one issue with your data. If you want the procedure to insert an average column for T = 25, then it will do so for all columns where T contains the string "25". If there are T= 8.25, 10.25, 15.25, etc, these will all have averages applied. The only way around it would be to include more of the heading string in the parameter array, but I presume you will be dealing with a variable Blood_Patient ID so that probably isn't an option.