how to select multiple cells using offset variable but keeping what is in top 2 rows selected - range

I want to multi select cell on a worksheet. I might start with selecting a2:g22 but next time I want to select a2:g2 and offset the remaining rows by 20 so they will become a23:g23. The offset will have a variable which will have 20 added to it each time the code runs.
NextRow = Range("ba2")
NextRow = NextRow + 20
Range("a2:g2,a3:g22").Offset(NextRow, 0).Select
If nextrow = 0 then range a2:g2 is selected and a3:g22 is selected then I add 20 to nextrow and I want a2:g2 to be selected and a23:g42 selected.
What I get instead is a22:g22 selected and a23:g42 selected.

After doing something else for a few hours I realized that offset will always work with the entire range whatever that might be. So I tried replacing the cell references in the range with a variable with only partial success. If I have a variable called SelRow and I put in it the cell references Will that work. So that code looked like this:
SelRow = "A23:G42"
Range("A2:G2",SelRow).Select
This didn't work because my selection was A2:G42 so I changed it to this:
SelRow = "A2:G2,A23:G42"
Range(SelRow).Select
And that worked. So all I needed to do was to start with a reference number that was used to calculate 2 more variables called StRow and EndRowThe reference number can and will be anything but for now I started with 23.
StRow=23
EndRow = StRow + 19
NowSel = "a2:G2,A" & StRow & ":G" & EndRow
Range(NowSel).Select
That worked. The result of NowSelin this case was A2:G2,A23:G42This can of course get quite long so my final code looks like this:
StRow = Worksheets("Cell List").Range("A1") 'Get the last row number used
StRow = StRow + 20 'Set the next start row to last row + 20
EndRow = StRow + 19 'Set the last row for selection to start row + 19
NowSel = "a2:c2,f2:g2,a" & StRow & ":c" & EndRow & ",f" & StRow & ":g" & EndRow
Range(NowSel).Select
And now the following cells are selected A2:C2and F2:G2 and A23:C42and F23:G42
Now I can create charts just by changing a reference number.

Related

Clear Contents of Specific Ranges

I am still new to VBA. I wanted to clear all the contents of the data (Row 3 to Row 12, Row 15 to Row 24, etc) below the yellow headers, without deleting all of the headers as shown in the photos (Fig 1 becomes Fig. 2). The headers go all the way down to row 109 (increments of 12 from Row 1, so Rows 1,13,25 ...85). I have a code but its too basic and long:
Sub Clear_All()
Set Unitsheet = ThisWorkbook.Worksheets("Sheet"1)
Unitsheet.Range("A3:F12").ClearContents
Unitsheet.Range("A15:F24").ClearContents
.
.
.
.'up to
Unitsheet.Range("A111:F120").ClearContents
End Sub
I need a code that is short, since the rows may reach up to more than 1000.
Any help will be much appreciated.
|
|
V
Sub clear()
Dim i, rows As Long
rows = ActiveSheet.UsedRange.rows.Count
For i = 1 To rows
If Sheet1.Cells(i, 1).Interior.ColorIndex = -4142 Then
Sheet1.Cells(i, 1).EntireRow.ClearContents
End If
Next
End Sub
this function finds all used rows in sheet1
it iterates all rows , if color of cell in A column has no color index (-4142) it clears all contents in entire row

VBA Autofilter subset contains extra row up top

The macro attempts to filter Sheet "Temp" for one Criteria at a time (PE, AR, DC,FI), and copy column 5 that contains non-duplicate data into another sheet "Detail". Please help me understand two issues. (1) The macro does correct filtering for each of the 4 criteria. However, the filtered list for each of the criteria always contains the first item from the filtered list of the very first criteria "PE". I.e. the filtered list for criteria 2, "AR", contains all items in AR, but starts with the first item in "PE". There's a header row, but it doesn't seem to make a difference. How can I get rid of that first item in all cases except when filtering for "PE" (where it belongs)? (2) I would like to be able to count and store the number of visible rows for each filtered list. I would like to be able to paste each filtered list into another spreadsheet ("Detail"), starting in cell A4. Each consecutive list should start two rows below the list that was just pasted. For example, if the first list contains 16 items, then the next list should start in cell A22 (A4+16+2). For some reason, copiedrows (used to remember number of rows in a filtered list) is correct the first time around (=16), but not the second time (=1?). It looks like q's 1 & 2 are related. Perhaps, if I figure out #1, I can do something about #2. I reviewed exiting posts on Autofiltering, but still feel a bit stuck here. Really appreciate your help!
Sub FilterCategories()
Dim LastRow As Long
Dim startpos As Integer
Dim k As Integer
Dim copiedrows(1 To 4) As Long
Dim AG(1 To 4) As String
Dim rng As Range
AG(1) = "PE"
AG(2) = "AR"
AG(3) = "DC"
AG(4) = "FI"
'Autofilter based on each AG and copy over to 'Detail'. Create temporary
sheet for filtering.
startpos = 4
For k = LBound(AG) To UBound(AG)
Application.DisplayAlerts = False
On Error Resume Next
Sheets("Temp").Delete
Sheets("Lookup").AutoFilterMode = False
Sheets("Lookup").Copy After:=Sheets("Lookup")
ActiveSheet.Name = "Temp"
With Sheets("Temp")
AutoFilterMode = False
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
With .Range("A2:E" & LastRow)
.AutoFilter Field:=4, Criteria1:=AG(k)
.RemoveDuplicates Columns:=5
.Columns(5).SpecialCells(xlCellTypeVisible).Copy
Destination:=Sheets("Detail").Range("A" & startpos)
copiedrows(k) = .SpecialCells(xlCellTypeVisible).Rows.Count
Debug.Print copiedrows(k)
startpos = copiedrows(k) + 6
Debug.Print startpos
End With
End With
Next
End Sub

Excel VBA DO loop to scan range of dates and fill in missing dates

I have a worksheet that has ~20,000 rows of data. Each row has a Transaction Date in Column C. Each worksheet will only include data from the previous month. What I am trying to have happen, is to have a loop run through each date and make sure there are no missing dates, if there is a missing date, I need the loop to insert the missing date into a new row. I have found this online and have tried to customize it to my worksheet but cannot get it to work:
Dim i As Long: i = 1
'Adds missing dates as new rows
Do
If Cells(i + 1, "C") > Cells(i, "C") + 1 Then
Rows(i + 1).Insert xlShiftDown
Cells(i + 1, "C") = Cells(i, "C") + 1
End If
i = i + 1
Loop Until Cells(i + 1, "C") = ""
I have a bunch of different macros that format the data into a table and then sort it by the transaction date. I'm not sure if the table creates another issue or not but I can't seem to get it to work.
Any tips are greatly appreciated!
Starting with data that looks like this:
Run this code to loop through the dates bottom up and insert missing rows.
Sub insertMissingDate()
Dim wks As Worksheet
Set wks = Worksheets("Sheet1")
Dim lastRow As Long
lastRow = wks.Range("C2").End(xlDown).Row
'Work bottom up since we are inserting new rows
For i = lastRow To 3 Step -1
curcell = wks.Cells(i, 3).Value
prevcell = wks.Cells(i - 1, 3).Value
'Using a loop here allows us to bridge a gap of multiple missing dates
Do Until curcell - 1 = prevcell Or curcell = prevcell
'Insert new row
wks.Rows(i).Insert xlShiftDown
'Insert missing date into new row
curcell = wks.Cells(i + 1, 3) - 1
wks.Cells(i, 3).Value = curcell
Loop
Next i
End Sub
Results:
A note to anyone using this code- you have to have your date formatted so that in single digit months, (i.e. January- example "1/1/17") you have a zero in front of the month, making it two digit (i.e. 01/1/17). The code will not work unless you do this. There is a data type under "more data formats" on the home page of excel that will include zeros in front of a single digit month. Thanks for the code, it's helping me tremendously!

DJing with MATLAB (Low-Level I/O)

Alrighty everybody, it's the time of the week where I learn how to do weird things with MATLAB. This week it's DJing. What I need to do is figure out how to make my function output the name of the song whose length is closest to the time left. For instance, if I'm showing off my DJing skills and I have 3:22 left, I have to pick a song whose length is closest to the time left (can be shorter or longer). I'm given a .txt file to choose from.
Test Case
song1 = pickSong('Funeral.txt', '3:13')
song1 => 'Neighborhood #2 (Laika)'
The file for this looks like:
1. Neighborhood #1 (Tunnels) - 4:48
2. Neighborhood #2 (Laika) - 3:33
3. Une annee sans lumiere - 3:40
4. Neighborhood #3 (Power Out) - 5:12
5. Neighborhood #4 (7 Kettles) - 4:49
6. Crown of Love - 4:42
7. Wake Up - 5:39
8. Haiti - 4:07
9. Rebellion (Lies) - 5:10
10. In the Backseat - 6:21
I have most of it planned out, what I'm having an issue with is populating my cell array. It only puts in the last song, and then changes it to a -1 after my loop runs. I've tried doing it three different ways, the last one being the most complex (and gross looking sorry). Once I get the cell array into it's proper form (as the full song list and not just -1) I should be in the clear.
function[song] = pickSong(file_name,time_remain)
Song_list = fopen(file_name, 'r'); %// Opens the file
Song_names = fgetl(Song_list); %// Retrieves the lines, or song names here
Songs_in = ''; %// I had this as a cell array first, but tried to populate a string this time
while ischar(Songs) %// My while loop to pull out the song names
Songs_in = {Songs_in, Songs};
Songs = fgetl(Song_list);
if ischar(Songs_in) %//How I was trying to populate my string
song_info = [];
while ~isempty(Songs_in)
[name, time] = strtok(Songs_in);
song_info = [song_info {name}];
end
end
end
[songs, rest] = strtok(Songs, '-');
[minutes, seconds] = strtok(songs, ':');
[minutes2, seconds2] = strtok(time_remain, ':')
all_seconds = (minutes*60) + seconds; %// Converting the total time into seconds
all_seconds2 = (minutes2*60) + seconds2;
song_times = all_seconds;
time_remain = all_seconds2
time_remain = min(time_remain - song_times);
fclose(file_name);
end
Please and thank you for the help :)
A troublesome case:
song3 = pickSong('Resistance.txt', '3:57')
song3 => 'Exogenesis: Symphony Part 2 (Cross-Pollination)'
1. Uprising - 5:02
2. Resistance - 5:46
3. Undisclosed Desires - 3:56
4. United States of Eurasia (+Collateral Damage) - 5:47
5. Guiding Light - 4:13
6. Unnatural Selection - 6:54
7. MK ULTRA - 4:06
8. I Belong to You (+Mon Coeur S'ouvre a Ta Voix) - 5:38
9. Exogenesis: Symphony Part 1 (Overture) - 4:18
10. Exogenesis: Symphony Part 2 (Cross-Pollination) - 3:57
11. Exogenesis: Symphony Part 3 (Redemption) - 4:37
Here is my implementation:
function song = pickSong(filename, time_remain)
% read songs file into a table
t = readSongsFile(filename);
% query song length (in seconds)
len = str2double(regexp(time_remain, '(\d+):(\d+)', ...
'tokens', 'once')) * [60;1];
% find closest match
[~,idx] = min(abs(t.Duration - len));
% return song name
song = t.Title(idx);
end
function t = readSongsFile(filename)
% read the whole file (as a cell array of lines)
fid = fopen(filename,'rt');
C = textscan(fid, '%s', 'Delimiter',''); C = C{1};
fclose(fid);
% parse lines of the form: "0. some name - 00:00"
C = regexp(C, '^(\d+)\.\s+(.*)\s+-\s+(\d+):(\d+)$', 'tokens', 'once');
C = cat(1, C{:});
% extract columns and create a table
t = table(str2double(C(:,1)), ...
strtrim(C(:,2)), ...
str2double(C(:,3:4)) * [60;1], ...
'VariableNames',{'ID','Title','Duration'});
t.Properties.VariableUnits = {'', '', 'sec'};
end
We should get the expected results on the test files:
>> pickSong('Funeral.txt', '3:13')
ans =
'Neighborhood #2 (Laika)'
>> pickSong('Resistance.txt', '3:57')
ans =
'Exogenesis: Symphony Part 2 (Cross-Pollination)'
Note: The code above uses MATLAB tables to store the data, which allows for easy manipulation. For example:
>> t = readSongsFile('Funeral.txt');
>> t.Minutes = fix(t.Duration/60); % add minutes column
>> t.Seconds = rem(t.Duration,60); % add seconds column
>> sortrows(t, 'Duration', 'descend') % show table sorted by duration
ans =
ID Title Duration Minutes Seconds
__ _____________________________ ________ _______ _______
10 'In the Backseat' 381 6 21
7 'Wake Up' 339 5 39
4 'Neighborhood #3 (Power Out)' 312 5 12
9 'Rebellion (Lies)' 310 5 10
5 'Neighborhood #4 (7 Kettles)' 289 4 49
1 'Neighborhood #1 (Tunnels)' 288 4 48
6 'Crown of Love' 282 4 42
8 'Haiti' 247 4 7
3 'Une annee sans lumiere' 220 3 40
2 'Neighborhood #2 (Laika)' 213 3 33
% find songs that are at least 5 minutes long
>> t(t.Minutes >= 5,:)
% songs with the word "Neighborhood" in the title
>> t(~cellfun(#isempty, strfind(t.Title, 'Neighborhood')),:)
I'm going to write an answer using most of what you have already written, instead of suggesting something completely different. Though regexp is a powerful too (and I like regular expressions), I find that it is too advanced for what you have learned so far, so let's scrap it for now.
This way, you get to learn what was wrong with your code, as well as how awesome of a debugger I am (just kidding). What you have when reading in the text file almost works. You made a good choice in creating a cell array to store all of the strings.
I'm also going to borrow MrAzzaman's logic in calculating the time in seconds through strtok (awesome job btw).
In addition, I'm going to change your logic a bit so that it makes sense to me on how I would do it. Here's the basic algorithm:
Open up the file and read the first line (song) as you did in your code
Initialize a cell array that contains the first song in the text file
Until we reach the end of the text file, read in the entire line and add it into the cell array. You've also noticed that as soon as you hit a -1, we don't have any more songs to read, so break out of the loop.
Now that we have our songs in a cell array, which include the track number, song and the time for each song, we are going to create two more cell arrays. The first one will store just the times of the songs as strings, with both the minutes and the seconds delimited by :. The next one will just contain the names of the songs themselves. Now, we go through each element in our cell array that we created from Step #3.
(a) To populate the first cell array, I use strfind to find all occurrences of where the - character occurs. Once I find where these occur, I choose the last location of where the - occurs. I use this to index into our song string, and skip over 2 characters to skip over the - character and the space character. We extract all of the characters from this point until the end of the line to extract our times.
(b) To populate the second cell array, I again use strfind, but then I figure out where the spaces occur, and choose the index of where the first space happens. This corresponds to the gap in between the song number and the track of the song. Using my result of the index from (a), I extract the song title by skipping one character from the index of the first space to the index two characters before the last - character to successfully get the song. This is because there will probably be a space in between the last word of the song title before the - character so we want to remove that space.
Next, for each song time in the first cell array computed in Step #4, I use strtok like you have used and split up the string by the :. MrAzzaman has used this as well and I'm going to borrow his logic on computing the total amount of seconds that each time takes.
Finally, we figure out which time is the closest to the time remaining. Note that we also need to convert the time remaining into seconds like we did in Step #5. As MrAzzaman has said, you can use the min function in MATLAB, and use the second output of the function. This tells you where in the array the minimum occurred. As such, we simply search for the minimum difference between the time remaining and the time elapsed for each song. Take note that you said you don't care whether or not you go over or under the time elapsed. You just want the closest time. In that case, you need to take the absolute value of the time differences. Let's say you had a song that took 3:59 and another song that was 6:00, and the time remaining was 4:00. Assuming that there is no song that is 4:00 long in your track, you would want to choose the song that is at 3:59. However, if you just subtract the time remaining from the longer track (6:00), you would get a negative difference, and min would return this track... not the song at 3:59. This is why you need to take the absolute value, so this will disregard whether you're over or under the time remaining.
Once we figure out which song to choose, return the song name that gives us the minimum. Make sure you close the file too!
Without further ado, here's the code:
function [song] = pickSong(file_name, time_remain)
% // Open up the file
fid = fopen(file_name, 'r');
%// Read the first line
song_name = fgetl(fid);
%// Initialize cell array
song_list = {song_name};
%// Read in the song list and place
%// each entry into a cell array
while ischar(song_name)
song_name = fgetl(fid);
if song_name == -1
break;
end
song_list = [song_list {song_name}];
end
%// Now, for each entry in our song list, find all occurrences of the '-'
%// with strfind, and choose the last index that '-' occurs at
%// Make sure you skip over by 2 spaces to remove the '-' and the space
song_times = cell(1,length(song_list));
song_names = cell(1,length(song_list));
for idx = 1 : length(song_list)
idxs = strfind(song_list{idx}, '-');
song_times{idx} = song_list{idx}(idxs(end)+2:end);
idxs2 = strfind(song_list{idx}, ' ');
%// Figure out the index of where the first space is, then extract
%// the string that starts from 1 over, to two places before the
%// last '-' character
song_names{idx} = song_list{idx}(idxs2(1)+1 : idxs(end)-2);
end
%// Now we have a list of times for each song. Tokenize by the ':' to
%// separate the minutes and times, then calculate the number of seconds
%// Logic borrowed by MrAzzaman
song_seconds = zeros(1,length(song_list));
for idx = 1 : length(song_list)
[minute_str, second_str] = strtok(song_times{idx}, ':');
song_seconds(idx) = str2double(minute_str)*60 + str2double(second_str(2:end));
end
%// Now, calculate how much time is remaining from the input
[minute_str, second_str] = strtok(time_remain, ':');
seconds_remain = str2double(minute_str)*60 + str2double(second_str(2:end));
%// Now, choose the song that is closest to the amount of time
%// elapsed
[~,song_to_choose] = min(abs(seconds_remain - song_seconds));
%// Return the song you want
song = song_names{song_to_choose};
%// Close the file
fclose(fid);
end
With your two example cases you've shown above, this is the output I get. I've taken the liberty in creating my own text files with your (awesome taste in) music:
>> song1 = pickSong('Funeral.txt', '3:13')
song1 =
Neighborhood #2 (Laika)
>> song2 = pickSong('Resistance.txt', '3:57')
song2 =
Exogenesis: Symphony Part 2 (Cross-Pollination)
You can manage this with textscan, as follows:
function[song,len] = pickSong(file_name,time_remain)
fid = fopen(filename);
toks = textscan(fid,'%[^-] - %d:%d');
songs = toks{1};
song_len = double(toks{2}*60 + toks{3});
[min_rem, sec_rem] = strtok(time_remain, ':');
time_rem = str2double(min_rem)*60 + str2double(sec_rem(2:end));
[len,i] = min(abs(time_rem - song_len));
song = songs{i};
Note that this will only work if none of your song names have a '-' character in them.
EDIT: Here's a solution that (should) work on any song titles:
function[song,len] = pickSong(file_name,time_remain)
file = fileread(file_name);
toks = regexp(file,'\d+. (.*?) - (\d+):(\d+)\n','tokens');
songs = cell(1,length(toks));
song_lens = zeros(1,length(toks));
for i=1:length(toks)
songs{i} = toks{i}{1};
song_lens(i) = str2double(toks{i}{2})*60 + str2double(toks{i}{3});
end
[min_rem, sec_rem] = strtok(time_remain, ':');
time_rem = str2double(min_rem)*60 + str2double(sec_rem(2:end));
[len,i] = min(abs(time_rem - song_lens));
song = songs{i};
regexp is a MATLAB function that runs regular expressions on a string (in this case your file of song names). The string '\d+. (.*?) - (\d+):(\d+)\n' scans each line extracting the name and length of each song. \d+ matches one or more digit, while .*? matches anything. The brackets are for grouping the output. So, we have:
match n digits, followed by a (string), followed by (n-digits):(n-digits)
Every thing in brackets is returned as a cell array to the toks variable. The for loop is just extracting the song names and lengths from the resulting cell array.

OpenOffice Compare two cell strings

So I am working on a macro in Basic, and where I'm at now I need to compare two columns for duplicates.
Here's what I have going so far:
for i = 0 To 5
Cell1 = Sheet.getCellByPosition(0,i)
for j = 0 to 5
Cell2 = Sheet.getCellByPosition(1,j)
rem COMPARISON WOULD HAPPEN HERE
Next j
Next i
I would like to do something along the lines of: if Cell1.String == Cell2.String then ...
This is my first attempt at writing a macro and so I would greatly appreciate any help and/or guidance.
Thanks!
Also on a side note if anyone know of good tutorials.documentation for this other than wiki, I would be extremely grateful for the link
You should store all values of the first column into an array and then compare every value of the second column with all entries of the array using a simple recursion.
A code that may work is
Dim firstcolumn(5) As String
For i = 0 to 5
firstcolumn(i) = Sheet.getCellByPosition(0,i)
Next i
For j = 0 to 5
Cell2 = Sheet.getCellByPosition(1,j)
for i = 0 to 5
if Cell2 = firstcolumn(i) then
MsgBox("The value of the cell " + i + " in the first column is the same with the value of the cell " + j + " of the second column")
Next i
Next j
The best place to look for code samples is the openoffice forum https://forum.openoffice.org/en/forum/
I hope that the above will help you.