I am reading an Excel file using good old OleDBConnections and OleDBCommands. How do I identify the used range of the excel sheet?
For example, if I were to use InterOp, I can always do this -
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xWb;
Excel.Worksheet xWs;
Excel.Range range;
xWb = xlApp.Workbooks.Open(#"D:\Technical\C#\WorkingFolder\HelloWorld.xls", 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
xWs = (Excel.Worksheet)xWb.Worksheets.get_Item(1);
range = xWs.UsedRange;
Now, I get a list of all the used columns. With OleDB, how do I get it. Now, even if I were to nail down the column list, how will I know that the rows end here.
Basically, my requirement is this.
I am about to receive an Excel (2003) file. The data will always include Headers (i.e., column name). It does not mean that the data will always be populated from A1:J200
It can also be from A5:J204 too.
So, in using OleDBCommand
string sConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Technical\C#\WorkingFolder\HelloWorld.xls;Extended Properties=""Excel 8.0;HDR=YES;""";
string sCommandString = "SELECT Employee_Id, First_Name FROM [Sheet1$A1:J200]";
OleDbConnection xlConnection = new OleDbConnection(sConnectionString);
xlConnection.Open();
OleDbCommand xlCommand = new OleDbCommand(sCommandString, xlConnection);
OleDbDataAdapter tempDataAdapter = new OleDbDataAdapter();
tempDataAdapter.SelectCommand = xlCommand;
DataTable tempDataTable = new DataTable("Whatever_I_Want");
tempDataAdapter.Fill(tempDataTable);
Now, the line
sCommandString has the hardcoded value of A1:J200, what if the excel I receive has the data range as A5:204, what will I do?
Any help would be great!
Untried but I think you could do it by first creating a DataAdapter with a SELECT * FROM [Sheet] statement and then use that in a call to FillSchema after which you should be able to call .Columns.Count on the DataTable you just filled.
Related
I have a Google sheet that is the answer sheet to a form sent to students for registration purposes. A= Timestamp B= Email address of student c= Students answer D= All emails of invited students What i need to achieve is column B to check if the email exists in column D and if so move the cell to column E. If it moves it would leave a gap so I would need it to move and then move cells up by 1. I can then import the cells into another sheet to see only the students who have not replied or is there a way to email those automatically.
Your assistance would be greatly appreciated
https://docs.google.com/spreadsheets/d/1XfyKP5EbxoGYOiWgPiRWA_OvPGN9hOjICXp_iuJrwIg/edit?usp=sharing
You want to do the following:
Remove values from column D that also exist in column B (shift up values so that no blank cells are kept).
Send emails to the resulting column D values.
If that's the case, you can do the following with Apps Script: create a bound script by selecting Tools > Script editor and copy and execute the following code (check inline comments):
function sendEmailsToNonRespondents() {
var ss = SpreadsheetApp.getActive(); // Get spreadsheet
var sheetName = "Sheet1"; // Your sheet name (change if necessary)
var sheet = ss.getSheetByName(sheetName); // Get sheet
var allAddresses = getColumnValues(sheet, 2, 4); // Get non-empty values from column D (excluding row 1)
var respondents = getColumnValues(sheet, 2, 2); // Get non-empty values from column B (excluding row 1)
var nonRespondents = allAddresses.filter(address => !respondents.includes(address)).map(address => [address]); // Retrieve list of emails of non-respondents
sheet.getRange("D2:D").clearContent(); // Clear old column D content (excluding row 1)
sheet.getRange(2, 4, nonRespondents.length, 1).setValues(nonRespondents); // Write new column D content (non-respondents)
nonRespondents.forEach(email => {
var subject = "Mail subject"; // Change according to your preferences
var body = "Mail body"; // Change according to your preferences
MailApp.sendEmail(email[0], subject, body); // Send email for each non-respondent
});
}
function getColumnValues(sheet, firstRow, colIndex) {
return sheet.getRange(firstRow, colIndex, sheet.getLastRow() - firstRow + 1, 1).getValues().filter(value => value[0] != "").map(value => value[0]);
}
Note:
In this sample, no data is moved to column E (I don't see why that's necessary, since the respondent emails are recorded in column B anyway).
In this sample, every time the script runs, all content in column D gets removed, and the filtered content is written again, instead of just removing the undesired values.
Reference:
Spreadsheet Service
MailApp.sendEmail(recipient, subject, body)
I'm pretty new to excel or google sheets. The work place, that I work at does not have anything stream lined.
I'm trying to create my own work book that I can refresh everyday I log in so that I can have a list of things that I need to work on for that day.
One of the functions that I would like to have is, whenever a new sheet is shared with me on Google Sheets, I want the URL for that sheet to populate in one of the cells in my workbook automatically and arranged based on timestamp.
I was trying to search for this on Google, but I read that: shared with me docs are not stored anywhere specifically.
Any help or pointing me in the right direction is highly appreciated.
It is easy to fetch the files that have been shared with you. For that, you can simply call the Drive API's Files: list method specifying in the q parameter the sharedWithMe attribute.
Afterwards, you can use the SpreadsheetApp class to gather a spreadsheet and insert data into it. In this case, you can simply make several calls of apendRow() to insert the results.
Finally, properties can be used to store the status of the script (last date checked), so that it can know where to resume from. In this case below, we'll be saving the date in the LAST_DATE property.
Code.gs
var SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID';
var SHEET_NAME = 'YOUR_SHEET_NAME';
function myFunction() {
var sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
var lastDate = new Date(PropertiesService.getScriptProperties().getProperty('LAST_DATE'));
var currentDate = new Date();
var files = getFiles(lastDate);
for (var i=0; i<files.length; i++) {
var row = [
new Date(files[i].sharedWithMeDate),
files[i].title,
files[i].alternateLink,
files[i].sharingUser.emailAddress,
files[i].owners.map(getEmail).join(', ')];
sheet.appendRow(row);
}
console.log('lastDate: %s, currentDate: %s, events added: %d', lastDate, currentDate, files.length);
PropertiesService.getScriptProperties().setProperty('LAST_DATE', currentDate.toISOString());
}
function getEmail(user) {
return user.emailAddress;
}
function getFiles(lastSharedDate) {
var query = "sharedWithMe and mimeType = 'application/vnd.google-apps.spreadsheet'";
var res = Drive.Files.list({
q: query,
orderBy: "sharedWithMeDate desc",
fields: "*",
pageSize: 1000
});
// `query` parameter cannot compare sharedWithMeDate, so we do it afterwards
return res.items.filter(function (i) {
return (new Date(i.sharedWithMeDate) > lastSharedDate);
}).reverse();
}
You can set up the script to be ran periodically (i.e once a day, or more in case you'd need it) using Time-driven triggers.
I want to generate excel sheet from EPPluse which has group column header like below picture.Then How to build DataTable which handle this situation. please help me to get it done easily.
Thanks in advance
You want to use the Merge property on the ExcelRange object.
Here's an example:
using (var pck = new ExcelPackage(new FileInfo(#"c:\temp\Book1.xlsx")))
{
var ws = pck.Workbook.Worksheets["Sheet1"] ?? pck.Workbook.Worksheets.Add("Sheet1");
var rng = ws.Cells["BO1:BQ1"];
rng.Merge = true;
rng.Value = "Answer for Att 3";
pck.Save();
}
You can use this code to write your group headers, then use ExcelRange's LoadFromDataTable() method to write your DataTable directly to the worksheet starting from cell A2
I have a google form and I would like to sort it's responses in a separate sheet on google sheets. The results of the form look sort of like this.
Id Job
1 Shelving, Sorting
2 Sorting
1 Cleaning, Shelving
3 Customer Service
2 Shelving, Sorting
which I would like to format into
Id Jobs
1 Cleaning, Shelving, Sorting
2 Shelving, Sorting
3 Customer Service
Is there a formula I can use to accomplish this, noting that it ignores duplicates and groups the different ids? Ordering of the jobs does not matter.
Working example here.
The code is like:
=unique(transpose(split(join(", ",filter(B1:B10,A1:A10=1)),", ")))
where
filter(B1:B10,A1:A10=1) gives you all the B values where A = 1
join(",", filter(...)) joins the list with the ", " separator (e.g. "apple, orange" and "kiwi" becomes "apple, orange, kiwi"
split(join(...)) splits the list into an array (e.g. back to [apple, orange, kiwi]
transpose(split(...)) converts the horizontal list to vertical list
unique(transpose(...)) gives you the unique values (unique() only works with vertical list)
After this, you need to transpose then join the list
Note you must keep the separator consistent (e.g. always "," or ", ")
This is Apps Script code instead of a function. To use it, you will need to use the Tools menu, and open the script editor. Then select the function name from the drop down list, and then click the "Run" button.
To use this code, you need to have a source and a destination sheet. You will need to change the sheet names in the code to your sheet names. In this code, the source sheet is named 'Data'. You will need to change that to the name of your source sheet. In this code, the destination sheet is named 'Output', and is at the bottom of the code. This code gets data starting in row two, and writes the output data starting in row two. I tested it with your values and it works.
function concatCellData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Data');
var colOneData = sh.getRange(2, 1, sh.getLastRow()-1, 1).getValues();
var colTwoData = sh.getRange(2, 2, sh.getLastRow()-1, 1).getValues();
var newData = [],
newDataColOne = [],
colOneValue,
existInNewData = false,
colB_One,
colB_Two,
dataPosition,
thisValue,
combinedArrays = [],
arrayTemp = [];
for (var i=0;i<colOneData.length;i+=1) {
colOneValue = colOneData[i][0];
dataPosition = newDataColOne.indexOf(colOneValue);
existInNewData = dataPosition !== -1;
if (!existInNewData) {//If it doesn't exist in the new data, just write the values
newDataColOne.push(colOneValue);
newData.push([colOneValue, colTwoData[i][0]]);
continue;
};
colB_One = [];
colB_Two = [];
combinedArrays = []
arrayTemp = [];
colB_One = colTwoData[i][0].split(",");
colB_Two = newData[dataPosition][1];
colB_Two = colB_Two.split(",");
var combinedArrays = colB_One.concat(colB_Two);
//Get unique values
for (var j=0;j<combinedArrays.length;j+=1) {
thisValue = combinedArrays[j].trim();
if (arrayTemp.indexOf(thisValue) === -1) {
arrayTemp.push(thisValue);
};
};
newData[dataPosition] = [colOneValue, arrayTemp.toString()]; //Overwrite existing data
};
ss.getSheetByName('Output').getRange(2, 1, newData.length, newData[0].length).setValues(newData);
};
I have the inline PowerQuery to automate my weekly reporting. Since I am new to power query I followed up this tutorial and try to add a custom column so I can use it to see week over week improvements, the thing is that the column that is added is not named "Week" but instead it is called the name of the file. From this webpage the second parameter is column name. I do not find why column name is filename instead of the name "week".
let ExcelFile = (FilePath, FileName) =>
let
Source = Folder.Files(FilePath),
File = Source{[#"Folder Path"=FilePath,Name=FileName]}[Content],
ImportedExcel = Excel.Workbook(File),
Sheet1 = ImportedExcel{[Name="Page1_1"]}[Data],
TableWithWeek = Table.AddColumn(Sheet1,"Week", each FileName),
TableWithoutHeader = Table.Skip(TableWithWeek,3),
FirstRowAsHeader = Table.PromoteHeaders(TableWithoutHeader)
in
FirstRowAsHeader
in
ExcelFile
This call:
FirstRowAsHeader = Table.PromoteHeaders(TableWithoutHeader)
will replace the column names you have with the values from the first row. Since the first value under the column "Week" is the filename, then your table will now use that filename as the column name.
You can fix this by adding the custom column after you use PromoteHeaders:
let ExcelFile = (FilePath, FileName) =>
let
Source = Folder.Files(FilePath),
File = Source{[#"Folder Path"=FilePath,Name=FileName]}[Content],
ImportedExcel = Excel.Workbook(File),
Sheet1 = ImportedExcel{[Name="Page1_1"]}[Data],
TableWithoutHeader = Table.Skip(Sheet1, 3),
FirstRowAsHeader = Table.PromoteHeaders(TableWithoutHeader),
TableWithWeek = Table.AddColumn(FirstRowAsHeader,"Week", each FileName),
in
TableWithWeek
in
ExcelFile