Fork me on GitHub
#beginners
<
2022-09-15
>
Drew Verlee06:09:08

I'm struggling to understand what the purpose of a "zipfile entry" is in the context of this function (in this https://github.com/komcrad/clj-file-zip):

(ns clj-file-zip.core
  (:import [java.util.zip ZipEntry ZipOutputStream ZipInputStream]
           [net.lingala.zip4j.core ZipFile]
           [net.lingala.zip4j.util Zip4jConstants]
           [net.lingala.zip4j.model ZipParameters])
  (:require [ :as io]))

(defn zip-folder
  "Reads contents of folder (could be a single file or dir) and writes all data to output (ZipOutputStream).
   Entry name is the name of zipfile entry."
  ([folder entry-name output]
   (if (.isDirectory folder)
     (loop [children (vec (.listFiles folder))]
       (if (> (count children) 0)
         (do (zip-folder (first children) (str entry-name "/" (.getName (first children))) output)
             (recur (rest children)))))
     (let [in (io/input-stream folder)
           entry (ZipEntry. entry-name)]
       (.putNextEntry output entry)
       (io/copy in output)
       (.closeEntry output)
       (.close in)))))
What more could be provided to a zip folder function then the folder and the name of the output? I feel like it might be better to just use sh in this case... What do you normally do when you want to zip files? I think this function might just be half done? Like did they mean to add another arity with just two args?

Drew Verlee06:09:40

ah, im probably just supposed to call "zip" not zip-folder directly. From the https://github.com/komcrad/clj-file-zip#example

Zip a folder recursively:

(zip "/tmp/folder" "/tmp/folder.zip")

walterl06:09:05

You're probably right. Another consequence of that entry-name parameter in zip-folder is that you can add files/dirs to the zip file under a path other than that of the input.

lread14:09:41

@U0DJ4T5U1 not a response to your question but related: you might also consider https://github.com/babashka/fs, it has some zip/unzip support and offers a nice general fs API.

skylize14:09:24

An entry here is a single file within a zip containing 1+ files. This fn is kind of weird. It walks through a dir, writing files, but ignoring the directories themselves. So entry-name is only used written files, and those names are all getting read from the File objects anyway. You could eliminate the entry-name param by just reading name from the`File` at the beginning.

(if (.isDirectory folder)
  ...
  (do (zip-folder (first children) (str entry-name "/" (.getName (first children))) output)
      ... )
  ...)

;; changes to ---->

(let [entry-name (str entry-name "/" (.getName (first children)))]
  (if (.isDirectory folder)
    ...
    (do (zip-folder (first children) output)
        ...)
    ...))
The only potential use-case added by having entry-name passed in comes form zipping a single non-dir file, instead of the apparent intended use of a directory. In such case, the file will get renamed inside the zip to entry-name instead of the current name on disk.

👍 1
Drew Verlee16:09:08

Thanks @UE21H2HHD i think moving forward i'm going to use babshak anytime i need to do stuff that's "easy" for me from the command line like this.

👍 1
Drew Verlee16:09:58

and thanks @U90R0EPHA that explanation make sense.

arielalexi13:09:23

hey, I have a question regarding converting from one datastructure to another. How do you convert a vector of vectors to a single vector? for example :

given data structure: [["AA"] ["BB"]]
final data structure: ["AA" "BB"]
thank you 🙃

Ferdinand Beyer13:09:10

E.g. flatten will remove all nesting. If you want a vector, combine it with vec:

(vec (flatten x))

walterl13:09:27

(reduce into [] [["AA"] ["BB"]])

Ferdinand Beyer13:09:13

Yeah, or:

user=> (into [] (mapcat seq) [["A"] ["B"]])
["A" "B"]
So many options. But I wouldn't rule out flatten just because someone wrote a blog post about it 🙃

💯 1
Ferdinand Beyer13:09:59

(`reduce into` is a bit weird as into uses reduce internally, plus transients)

hoppy13:09:07

(mapv first <urvec>)

pavlosmelissinos13:09:34

The point of "avoid flatten" is that if you find you need to use it, the problem is usually somewhere else, e.g. it's very likely that [["foo" "bar"]["baz"]] is the result of having used map earlier instead of mapcat. Or if it's external input, there is probably a schema, so you already know how to bring the data to a more flexible form, flatten can be too destructive. The takeaway isn't "never use flatten" but "think very hard about whether you really need it".

Ferdinand Beyer13:09:37

Yeah, sure. If you don't now about mapcat and end up with too much nesting you should probably not get into the habit of just calling flatten on the result. However, if you have nested data that you want to denest, well, use flatten? The same advice holds for all suggested alternatives by the way.

☝️ 2
tomd13:09:42

(into [] cat foo)

🙌 3
☝️ 2
tomd13:09:29

Sure, sorry. cat is a transducer which concatenates the contents of each input. That means it unpacks each inner vector and puts the contents of each into [] which is better than flatten because it only unpacks one level, so you could safely have collections rather than strings.

tomd13:09:01

Agree that flatten should nearly always be avoided. It's a code smell imo

arielalexi14:09:28

(into [] cat [["AA"]["BB"]["CC"]]) ;=> ["AA" "BB" "CC"]

arielalexi14:09:27

thank you everyone 🙃

🙂 1
didibus00:09:35

Flatten flattens a deeply nested structure. If you didn't expect that, flatten could introduce an accidental bug, if later you happen to nest more things that you didn't want to flatten. That's the reason people say to "avoid" flatten. But it's bad advice, the reality is, don't flatten more levels than you need unless that's what you want. So use flatten to flatten all levels. And when it comes to flattening deeply, the question becomes, what do you want to flatten? Like only vectors, or also seqs, maps, strings, arrays, etc. Or only some of those? So flatten will flatten all sequential?, and maybe that's not what you want. There's nothing wrong with flatten, other than, it might not do what you want even if it can fool you as if it does on trivial examples.

sheluchin15:09:54

When should a set always be used as a predicate when checking for equality? e.g. (#{:x} thing) vs. (= :x thing).

Ben Sless16:09:18

When it's more than one thing

Ben Sless16:09:34

Or when you want to use it as an argument to filter or comp

💯 1
quoll00:09:33

The filter/comp thing is because = returns true/false while a singleton set returns the value. For this reason, it’s also useful for some

gratitude-thank-you 1
quoll00:09:45

Sorry I’m late. I haven’t looked here in a while 🙂

Ryan19:09:17

How would I most idomatically go about taking 3 vectors and finding each product of items in the other vectors at the same index? (e.g. [1 2] [1 2] = [1 4])

Darin Douglass19:09:47

you can supply multiple collections to map https://clojuredocs.org/clojure.core/map

thumbnail19:09:58

I.e. (map * a b c)

Darin Douglass19:09:15

❯ clj
Clojure 1.10.3
user=> (map * [1 2] [1 2])
(1 4)
user=>

2
☝️ 1
Ryan19:09:57

That is amazing