Формат CSV - это массив массивов. Каждый элемент основного массива представляет собой одну строку. Одна строка - это массив ячеек. Итак, в общем, вы хотите что-то вроде этого:
[
[:price, :brand],
["400", "Primark"],
["1000", "Pull&Bear"]
]
Вы можете достичь этого следующим образом:
headers = hash.values[0].keys # this will return [:price, :brand] and we'll use it as a header
data = hash.values.map(&:values)
csv_output = CSV.generate do |csv|
csv << headers # we need headers only once, we don't need them in every row
data.each do |single_row| # iterate over each row
csv << single_row # add the row to csv file
end
end
File.write('the_file.csv', csv_output)
Этот код предполагает, что для каждой строки у вас будут все данные доступно (т.е. в каждой строке будет указана цена и марка). Код выше, предоставленный переписанным пользователем, более гибок.
В ClickOnce правила папок несколько отличаются от обычных приложений Windows. Папка с данными, в которой развернуты файлы содержимого ClickOnce, находится в каталоге c: \ Users \ me \ Local Settings \ Apps \ 2.0 \ Data. Под этой папкой есть пара уровней подпапок с довольно загадочными идентификаторами.
Таким образом, фактическую папку данных для данного приложения ClickOnce лучше всего извлекать с использованием класса ApplicationDeployment . Вам также следует проверить свойство IsNetworkDeployed, чтобы убедиться, что вы работаете в развернутом режиме:
if (ApplicationDeployment.IsNetworkDeployed)
{
var dataDirectory = ApplicationDeployment.CurrentDeployment.DataDirectory;
...
}
Поскольку DataDirectory определяется ClickOnce, вы не можете жестко закодировать этот путь в конфигурацию log4net. Мое предложение было бы изменить пути к файлам программно при запуске приложения.
foreach(var appender in LogManager.GetRepository().GetAppenders())
{
var fileAppender = appender as FileAppender;
if (fileAppender != null)
{
fileAppender.File =
fileAppender.File.Replace("${LOCALAPPDATA}", dataDirectory);
fileAppender.ActivateOptions();
}
}