2

I have a program written in C# which should save a zip file every n records (like 500).

My idea was using the mod operator (%) and where the result of the operation is zero then write the file. Which is good, but: what if I have 520 records? I should write 500 files inside the first zip and then 20 file on the second one.

Here the code:

        using (ZipFile zip = new ZipFile())
        {
            zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Level8;
            zip.CompressionMethod = CompressionMethod.Deflate;
            int indexrow = 0;
           foreach(DataRow row in in_dt.Rows)
            {
                zip.AddFile(row["Path"].ToString(),"prova123");
                if(indexrow % 500 == 0)
                {
                    using (var myZipFile = new FileStream("c:\\tmp\\partial_"+indexrow.ToString()+".zip", FileMode.Create))
                    {
                        zip.Save(myZipFile);
                    }
                    indexrow = indexrow++;
                }
            }
        }
    }

in_dt is a datatable which contains all the file paths on filesystem.

zip object is an object based on the dotnetzip library.

LuciferSam
  • 313
  • 5
  • 20
  • First this won't work because your indexrow will just bounce between 0 and 1 since you declare it inside the loop. Secondly, what you want to do is, at the end of the loop (outside it), if the indexrow % 500 != 0, then you know you have some hanging files, so save the zip file. Also, I haven't used dotnetzip before, but I would assume you need to declare a new `ZipFile` after each save since, how would it know to remove all the files you called `AddFile` on already? – dmeglio Mar 06 '17 at 15:40
  • I'm not sure i understand, but if i'm right what you want is after `indexrow = indexrow++;` check something like `if (in_dt.Rows.Count - indexrow<500) { //create the second zip with the rest of files};` – Pikoh Mar 06 '17 at 15:42
  • @Pikoh that wouldn't work at all. Your if statement would return true 499 times if I have 500 rows. – dmeglio Mar 06 '17 at 15:44
  • Of course. That's what op wants if i get him right – Pikoh Mar 06 '17 at 15:45
  • @Pikoh If he has 520 files, he wants to create 2 zip files. You will create 500 zip files. I do not think that is what he wants. – dmeglio Mar 06 '17 at 15:46
  • yup, that's right. 520 files : 500 on 1st zip, 20 on 2nd zip. If I have 1020 = 500, 500 and 20 . But with the first suggestion I had some ideas. Thanks. – LuciferSam Mar 06 '17 at 15:47
  • I should also add `indexrow = indexrow++;` is unnecessary, `indexrow++` increments and assigns the value to the variable. – dmeglio Mar 06 '17 at 15:48
  • Possible duplicate of [Linq Select 5 items per Iteration](http://stackoverflow.com/questions/11427413/linq-select-5-items-per-iteration) – NineBerry Mar 06 '17 at 15:48
  • Yeah,but in the if i was not pretending to create a new zip file for each record,but something like adding it to a list and after the loop create a zip with all of them – Pikoh Mar 06 '17 at 15:50
  • Anyway,OP requirements are far from clear... – Pikoh Mar 06 '17 at 15:53
  • Does this answer your question? [Split a List into smaller lists of N size](https://stackoverflow.com/questions/11463734/split-a-list-into-smaller-lists-of-n-size) – xdtTransform Jun 09 '20 at 14:53
  • And https://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq – xdtTransform Jun 09 '20 at 14:54

2 Answers2

2

I'd use LINQ for this problem:

// Define the group size
const int GROUP_SIZE = 500;

// Select a new object type that encapsulates the base item
// and a new property called "Grouping" that will group the
// objects based on their index relative to the group size
var groups = in_dt
    .Rows
    .AsEnumerable()
    .Select(
        (item, index) => new {
            Item = item,
            Index = index,
            Grouping = Math.Floor(index / GROUP_SIZE)
        }
    )
    .GroupBy(item => item.Grouping)
;

// Loop through the groups
foreach (var group in groups) {
    // Generate a zip file for each group of files
}

For files 0 through 499, the Grouping property is 0.

For files 500 - 520, the Grouping property is 1.

peaceoutside
  • 942
  • 7
  • 12
  • 1
    seems interesting. Just a question since in_dt is a datatable from a XML File the compiler says: "DataRowCollection does not contain a 'select' definition". Should I change the datatable with "AsEnumerable()"? – LuciferSam Mar 06 '17 at 16:14
  • Yes, sorry about that, I was using notepad to write code and forgot about that, but yes, you're right: http://stackoverflow.com/questions/10855/linq-query-on-a-datatable - using .AsEnumerable() is the correct thing to do. I'll edit my code. – peaceoutside Mar 06 '17 at 16:23
  • 1
    With this answer it worked like a charm, and I learned something new. thanks ! – LuciferSam Mar 07 '17 at 08:57
  • No problem, I'm glad I was able to help you learn something new. – peaceoutside Mar 07 '17 at 14:47
1

What you probably want to do is something like this:

zipFiles(File[] Files, int MaxFilesInZip)
{
 int Parts = Files.Count / MaxFilesInZip;
 int Remaning = Files.Count % MaxFilesInZip;
 for(int i = 0; i < Parts; i++)
    //New zip
    for(int u = 0; u < MaxFilesInZip; u++)
       //Add Files[i*MaxFilesInZip + u]
 //New Zip
 //Add 'Remaning' amount of files
}

This way if you run the function like ths: zipFiles(520, 250), you would have 2*250 zip files and 1*20 with the remaning. You might have to work something with value on Parts (Floor/Celling).

Pau C
  • 773
  • 4
  • 20