最近查資料學會了C# Read Excel,可是找不到如何將相同的名稱做合併(非合併儲存格)
以下是我的Read Excel
我不知道怎麼讓他變成只要號碼重複,重複的行就會變成相加後的行。(如上面的圖)
謝謝各位的回覆,我可能沒有講得很清楚,因為我要想要處理好幾百行、幾百列的資料,然後並不是複製到另一邊,是想要直接重複貼到新的LIST
我更新一下問題,可能有點麻煩,我本來想要用List來做處理,如果有分配好各個名字,或者一組。
首先有兩個Excel,要做合併,然後是指定欄位,以及計算。
我想要將兩張表合再一起。
以有藍色的圖表"標題 im" 那個為主。
兩張表共通點是黃色。
第一張表只要黃色、藍色、紅色
第二張表只要黃色、綠色、紅色
特別注意是紅色,是相同im(名稱)做相加,兩張表的紅色欄位相加
我之前的想法是將兩張表的資料都丟到LIST,處裡完在做輸出。學長也是叫我這樣做。
發現三張表欄位名稱有整個不同。
想參考我的解法,請先去VS裡的nuget安裝epplus免費的Excel讀寫工具(效能比office快很多,因為是純xml讀寫,上千行的xlsx檔讀寫小於10秒,執行時期可免安裝office)
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
private void Form1_Load(object sender, EventArgs e)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage ep = new ExcelPackage(new System.IO.FileInfo(@"s:\test\test.xlsx")))
{
// 準備收集所有資料用
var dataOrigin = new List<KeyValuePair<string, int>>();
// 範例當然是放在第一個工作表
ExcelWorksheet sh = ep.Workbook.Worksheets[0];
// 先讀取這個xlsx檔有多少列
int totalRows = sh.Dimension.Rows;
for (int r = 2; r <= totalRows;r++)
{
dataOrigin.Add(new KeyValuePair<string, int>(sh.Cells[r, 1].Value.ToString(),Convert.ToInt32(sh.Cells[r, 2].Value)));
}
// 清空已經存在的轉換後結果
sh.Cells[2, 4, totalRows, 5].Clear();
// 把收集到的資料以編號做成群組(GroupBy)並按編號排序(OrderBy)
var dataOutput = dataOrigin.GroupBy(g => g.Key).OrderBy(s => s.Key);
int R = 2; //因為用foreach所以自己加index
foreach(var d in dataOutput)
{
sh.Cells[R, 4].Value = d.Key; // 輸出編號
sh.Cells[R, 5].Value = d.Sum(s => s.Value); // 輸出這個編號的加總
R++;
}
ep.Save();
}
}
輸出前
輸出結果
LINQ/LAMBDA很重要,LINQ/LAMBDA很重要,LINQ/LAMBDA很重要
一定要學好,不然資料一多一定會搞慘你
python也有免安裝OFFICE的OpenXYL可用,一樣簡單
好的,看了您的方式讓我學到一課,不過我可能沒講清楚題目,我是要一次做很多資料,可是想了兩天,還是無法,很多可以用到的指令還不是很熟。
分組的方法就一個 .GroupBy,我就依編號(key)來分群,每筆key裡有所有的成員,如編號1有2筆100,所以sum出來的結果就是200,以此類推,大量的資料也一樣,不會走鐘
恩,我後來看了KeyValuePair,如果我欄位想要好幾個,這個如何使用呢?
就只用List解決問題,只是讀寫office的方式比較與眾不同
那就不用KeyValuePair,需要自建Class,我就命名為ManyKeysValue
class ManyKeysAndValue
{
public List<object> keys { get; set; }
public object value { get; set; }
public ManyKeysAndValue(object[] _keys, object v)
{
keys = new List<object>();
keys.AddRange(_keys);
value = v;
}
}
主程式要修改一下符合"完全可變長度陣列"當做KEY的的要求
private void Form1_Load(object sender, EventArgs e)
{
goExcel();
}
private void goExcel()
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage ep = new ExcelPackage(new System.IO.FileInfo(@"s:\test\test.xlsx")))
{
// 準備收集所有資料用
var dataOrigin = new List<ManyKeysAndValue>();
// 範例當然是放在第一個工作表
ExcelWorksheet sh = ep.Workbook.Worksheets[0];
// 先讀取這個xlsx檔有多少列
int totalRows = sh.Dimension.Rows;
for (int r = 2; r <= totalRows; r++)
{
dataOrigin.Add(
new ManyKeysAndValue(
new object[] {
sh.Cells[r,1].Value,
sh.Cells[r,2].Value,
sh.Cells[r,3].Value,
sh.Cells[r,4].Value,
sh.Cells[r,5].Value,
// 從第A列到第E列都是你的KEY組
},
sh.Cells[r, 6].Value) // 你要加總的欄位
);
}
// 清空已經存在的轉換後結果
sh.Cells[2, 12, totalRows, sh.Dimension.Columns].Clear();
// 把收集到的資料以編號做成群組(GroupBy)並按編號排序(OrderBy)
var dataOutput =
dataOrigin
.GroupBy(g => (string)g.keys[2]) // 0,1,2....2→居住地
.OrderBy(s => s.Key);
int R = 2; //因為用foreach所以自己加index
foreach (var d in dataOutput)
{
sh.Cells[R, 12].Value = d.Key; // 輸出編號
sh.Cells[R, 13].Value = d.Sum(s => Convert.ToInt32(s.value)); // 輸出這個編號的加總
R++;
}
ep.Save();
}
}
照你的想法改成多格資料的EXCEL,以下假設以行政區加總每個區的銷售營業額(單位為萬元),加總前...
加總後...
謝謝,又學到新的指令,我剛剛有更新的問題,請問如果是您會如何處理呢?
其實你要的是加總吧,
不懂你的問題在哪,
既然你都用List來存資料了,
就做完計算之後,
再一列一列把資料寫出來就好了.
我照著你的輸入,發現好像格子為NULL,會無法處理
我照你更新的 excel 做處理,沒問題
void Main()
{
var path = @"C:\Users\Wei\Downloads\TestIssue232.xlsx";
var rows = MiniExcel.Query(path,useHeaderRow:true);
var newInput = (from s in rows
group s by s.Number into g
select new {
Number=g.Key,
Apr=g.Sum(_=>(decimal?)_.Apr),
May=g.Sum(_=>(decimal?)_.May),
Jun=g.Sum(_=>(decimal?)_.Jun),
}
);
Console.WriteLine(newInput);
MiniExcel.SaveAs("output.xlsx",newInput);
}
這樣嗎?
可以嘗試使用我寫的一個小工具 MiniExcel
安裝 nuget 包
Install-Package MiniExcel -Version 0.13.3
代碼
void Main()
{
var path = @"C:\Users\Wei\Downloads\Book5.xlsx";
var rows = MiniExcel.Query(path,true);
var outputPath = "output2.xlsx";
MiniExcel.SaveAs(outputPath, rows.GroupBy(g => g.號碼).Select(s => new {號碼=s.Key,金額=s.Sum(_=>(int)_.金額)}));
}
但牽扯到修改樣式建議還是使用 epplus
謝謝你的回答,您的工具我也有用過,只是不知道語法,以及做很多行列如何寫(範例只有兩列),我照著你的輸入,發現好像格子為NULL,會無法處理
菜鳥獵人
我這邊測試沒有問題
過程我更新在內文了
好的,謝謝你,我其實是有一個比較大的問題,已經更新了,因為本來想說先慢慢處理,後來學長跟我說不用那麼麻煩,全部丟到List做處理(學長不是用C#)是其他語言。
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var transactions = new List<Transaction>{};
transactions.Add(new Transaction { Category = "1", Amount = 100 });
transactions.Add(new Transaction { Category = "2", Amount = 200 });
transactions.Add(new Transaction { Category = "2", Amount = 100 });
transactions.Add(new Transaction { Category = "3", Amount = 300 });
transactions.Add(new Transaction { Category = "4", Amount = 100 });
transactions.Add(new Transaction { Category = "5", Amount = 400 });
transactions.Add(new Transaction { Category = "6", Amount = 100 });
transactions.Add(new Transaction { Category = "5", Amount = 400 });
transactions.Add(new Transaction { Category = "1", Amount = 100 });
transactions.Add(new Transaction { Category = "2", Amount = 200 });
var summaryApproach1 = transactions.GroupBy(t => t.Category)
.Select(t => new
{
Category = t.Key,
Amount = t.Sum(ta => ta.Amount),
}).ToList();
Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}"));
var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
var transactionArray = t as Transaction[] ?? t.ToArray();
return new
{
Category = key,
Amount = transactionArray.Sum(ta => ta.Amount),
};
}).ToList();
Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}"));
}
}
public class Transaction
{
public string Category { get; set; }
public decimal Amount { get; set; }
}
謝謝你的回答,因為我也看過這種類似的文章,只是不知道要怎麼讓他分好,而不是自己手動輸入分好組。
我改一個版本給你參考
我想問一下。
transactions.Add(new Transaction { Category = "1", Amount = 100 });
這些一定要手動打嗎?因為資料可能不是這些。
可能上白行。
這段你要自己改
把 "1" 和 100 改成你從 Excel 讀到的資料
大概像這樣
for (int i = 1; i <= rowCount;i++) {
transactions.Add(new Transaction { Category = xlRange.Cells[i, 1], Amount = xlRange.Cells[i, 2] });
}