Reputation: 1824
I am trying to recursively call a function with arguments that are node :type
folder
using an if
statement to conditionally filter them out. I am however getting the error:
Uncaught Error: No item :type in vector of length 3
I thought this might be because of empty :children
vectors but even when there is a :type
property in them, I still get the error. The error is kind of cryptic and I cannot see a vector without a :type
keyword.
Here is my code:
(defn dispatch-nodes-to-state [node & {:keys [depth] :or {depth 0}}]
(re-frame/dispatch [:set-nodes (conj {:depth depth} node)])
(if (= (node :type) "folder")
(do
(println "It's a folder")
(recur (node :children) (inc depth)))
(println "it's a file")))
(defn traverse-tree [nodes]
(run! dispatch-nodes-to-state nodes))
(defn get-file-tree []
(go (let [file-tree (<! (http/get API-URL request-opts))]
(traverse-tree (get-in file-tree [:body :root])))))
Is there something obviously wrong with the code? Here is a snippet of the data structure I'm traversing. Thanks in advance:
[{:depth 0, :type folder, :empty false, :name My Documents, :children [{:type file, :name My Text 1, :extension .txt, :preview In ipsum est, faucibus ac tempor vel, tincidunt ac leo. Vestibulum quis odio nisl. Nulla auctor erat a sem ullamcorper vulputate. Fusce efficitur tempus nulla, eget suscipit tortor imperdiet nec. Aenean finibus, ipsum ut pulvinar euismod, neque tellus vestibulum justo, sit amet volutpat justo lectus nec risus.} {:type folder, :empty false, :name My Music, :children [{:type folder, :empty false, :name Pop, :children [{:type folder, :empty false, :name Rock, :children [{:type folder, :empty true, :name Empty Folder, :children [{:type yes}]} {:type file, :name Lyrics, :extension .txt, :preview Curabitur molestie ex ligula, ac tincidunt nisi tempor sit amet. Nullam eget varius purus, nec lobortis urna. Pellentesque consequat est fringilla, mollis turpis at, consequat est. In sit amet lectus dui. Vestibulum sodales ligula at diam lacinia iaculis. Cras molestie dui at odio vestibulum feugiat.}]} {:type file, :name Song Lyrics, :extension .txt, :preview Nam venenatis nunc eget imperdiet laoreet. Vivamus luctus luctus neque in luctus. Donec convallis, elit non lobortis molestie, felis tellus molestie nunc, vel condimentum ante enim sit amet augue.}]} {:type file, :name Album Artwork, :extension .png, :preview photos.google.com/abc.jpg} {:type folder, :empty false, :name Acoustic, :children [{:type folder, :empty false, :name Guitar Music, :children [{:type file, :name Fav song lyrics, :extension .txt, :preview Mauris pharetra suscipit purus et molestie. Cras rhoncus est enim, pulvinar tristique orci sagittis non. Sed volutpat tellus id elit laoreet faucibus a quis eros. Sed cursus fermentum ultrices. Praesent blandit luctus feugiat. Nunc est massa, volutpat sed justo sagittis, ultricies pellentesque enim. Mauris lacinia enim ac erat sodales, in feugiat nulla auctor.}]} {:type file, :name song artwork, :extension .png, :preview photos.google.com/abc.jpg}]} {:type file, :name reminder, :extension .txt, :preview Aenean orci dui, dictum at odio sed, malesuada placerat erat. Aliquam eget risus nec lorem convallis commodo. Praesent tincidunt a ligula sed fringilla. Maecenas posuere sem a scelerisque auctor. Nam ultrices maximus elit, quis efficitur nunc auctor ac.}]} {:type file, :name Recipe, :extension .txt, :preview Aliquam bibendum lacus ex, at malesuada purus maximus eget. Mauris viverra diam vel turpis imperdiet dictum. Nam vel augue non diam egestas pulvinar nec nec odio. Nulla tincidunt interdum lorem, vitae tincidunt massa elementum vitae. Nam varius vitae nisl a ullamcorper. Integer dui mauris, vulputate ac sem ac, varius dapibus erat. Ut pharetra ultrices velit, mattis elementum velit varius in. Donec varius turpis purus, sit amet ultricies ipsum elementum nec.}]} {:depth 0, {:type file, :name My Text 1, :extension .txt, :preview In ipsum est, faucibus ac tempor vel, tincidunt ac leo. Vestibulum quis odio nisl. Nulla auctor erat a sem ullamcorper vulputate. Fusce efficitur tempus nulla, eget suscipit tortor imperdiet nec. Aenean finibus, ipsum ut pulvinar euismod, neque tellus vestibulum justo, sit amet volutpat justo lectus nec risus.} {:type folder, :empty false, :name My Music, :children [{:type folder, :empty false, :name Pop, :children [{:type folder, :empty false, :name Rock, :children [{:type folder, :empty true, :name Empty Folder, :children [{:type yes}]} {:type file, :name Lyrics, :extension .txt, :preview Curabitur molestie ex ligula, ac tincidunt nisi tempor sit amet. Nullam eget varius purus, nec lobortis urna. Pellentesque consequat est fringilla, mollis turpis at, consequat est. In sit amet lectus dui. Vestibulum sodales ligula at diam lacinia iaculis. Cras molestie dui at odio vestibulum feugiat.}]} {:type file, :name Song Lyrics, :extension .txt, :preview Nam venenatis nunc eget imperdiet laoreet. Vivamus luctus luctus neque in luctus. Donec convallis, elit non lobortis molestie, felis tellus molestie nunc, vel condimentum ante enim sit amet augue.}]} {:type file, :name Album Artwork, :extension .png, :preview photos.google.com/abc.jpg} {:type folder, :empty false, :name Acoustic, :children [{:type folder, :empty false, :name Guitar Music, :children [{:type file, :name Fav song lyrics, :extension .txt, :preview Mauris pharetra suscipit purus et molestie. Cras rhoncus est enim, pulvinar tristique orci sagittis non. Sed volutpat tellus id elit laoreet faucibus a quis eros. Sed cursus fermentum ultrices. Praesent blandit luctus feugiat. Nunc est massa, volutpat sed justo sagittis, ultricies pellentesque enim. Mauris lacinia enim ac erat sodales, in feugiat nulla auctor.}]} {:type file, :name song artwork, :extension .png, :preview photos.google.com/abc.jpg}]} {:type file, :name reminder, :extension .txt, :preview Aenean orci dui, dictum at odio sed, malesuada placerat erat. Aliquam eget risus nec lorem convallis commodo. Praesent tincidunt a ligula sed
This is what I'm trying to achieve in Javascript:
const traverseTree = (nodes, depth = 0) => {
nodes.forEach(node => {
updateNodes({...node, depth})
if (node.type === 'folder') {
traverseTree(node.children, depth + 1)
}
})
}
traverseTree(fileTree.root)
};
I did make this work in the end by using doseq
like in the answer below. However, I had to get rid of the function using run!
and skip the error 2
fix below as suggested:
(defn dispatch-nodes-to-state [nodes & {:keys [depth] :or {depth 0}}]
(doseq [node nodes]
(if (= (node :type) "folder")
(do
(re-frame/dispatch [:set-nodes (conj {:depth depth} node)])
(dispatch-nodes-to-state (node :children) :depth (inc depth)))
(do
(re-frame/dispatch [:set-nodes (conj {:depth depth} node)])
(dispatch-nodes-to-state (node :children) :depth (inc depth))))))
(defn get-file-tree []
(go (let [file-tree (<! (http/get API-URL request-opts))]
(dispatch-nodes-to-state (get-in file-tree [:body :root])))))
Upvotes: 0
Views: 111
Reputation: 29958
Here is how I would do it, without your re-frame details:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[clojure.walk :as walk]
[tupelo.string :as ts]))
I added 2 versions of your data
(def data-1
{:depth 0, :type "folder", :empty false, :name "My Documents",
:children [{:type "file", :name "My Text" 1, :extension ".txt, :preview" " In ipsum est, faucibus ac"}
{:type "folder", :empty false, :name "My Music",
:children [{:type "folder", :empty false, :name "Pop",
:children [{:type "folder", :empty false, :name "Rock",
:children [{:type "folder", :empty true, :name "Empty Folder", :children [{:type "yes"}]}
{:type "file", :name "Lyrics", :extension ".txt", :preview "Curabitur molestie ex ligula, ac tincidunt nisi"}]}
{:type "file", :name "Song Lyrics", :extension ".txt", :preview "Nam venenatis nunc eget imperdiet laoreet. Vivamus"}]}
{:type "file", :name "Album Artwork", :extension ".png", :preview "photos.google.com/abc.jpg"}]}
{:type "file", :name "Recipe", :extension ".txt", :preview " Aliquam bibendum lacus ex",}]})
and the 2nd version
(def data-2
{:type "folder",
:empty "false",
:name "My Documents",
:children [{:type "file",
:name "My Text 1",
:extension ".txt",
:preview "In ipsum est, faucibus ac tempor vel, tincidunt ac leo. Vestibulum quis odio nisl. Nulla auctor erat a "}
{:type "folder",
:empty "false",
:name "My Music",
:children [{:type "folder",
:empty "false",
:name "Pop",
:children [{:type "folder",
:empty "false",
:name "Rock",
:children [{:type "folder",
:empty "true",
:name "Empty Folder",
:children []}
{:type "file",
:name "Lyrics",
:extension ".txt",
:preview "Curabitur molestie ex ligula, ac tincidunt nisi tempor sit amet. Nullam eget varius at."}]}
{:type "file",
:name "Song Lyrics",
:extension ".txt",
:preview "Nam venenatis nunc eget imperdiet laoreet. Vivamus luctus luctus neque "}]}]}]})
basic code to print the name & depth using prewalk
(defn print-name-depth
[node]
(with-result node ; don't change node
(if (map? node)
(let [name (:name node)
depth (:depth node)]
(spyx [name depth])))))
How to recursively add depth to each node & its children
(defn add-depth-to-node
[depth node]
(glue node {:depth depth ; add/overwrite dept for current node
:children (let [depth-kids (inc depth)] ; new depth for child nodes
(forv [child (:children node)]
(add-depth-to-node depth-kids child)))}))
And run the whole thing in a test file (w/o actually testing, just printing)
(dotest
(newline)
(let [data-1b (spyx-pretty (add-depth-to-node 0 data-1))
>> (newline)
data-2b (spyx-pretty (add-depth-to-node 0 data-2))]
(newline)
(walk/prewalk print-name-depth data-1b)
(newline)
(walk/prewalk print-name-depth data-2b)))
And the results:
-----------------------------------
Clojure 1.10.1 Java 12.0.1
-----------------------------------
Testing tst.demo.core
(add-depth-to-node 0 data-1) =>
{:depth 0,
:type "folder",
:empty false,
:name "My Documents",
:children
[{:type "file",
:name "My Text",
1 :extension,
".txt, :preview" " In ipsum est, faucibus ac",
:depth 1,
:children []}
{:type "folder",
:empty false,
:name "My Music",
:children
[{:type "folder",
:empty false,
:name "Pop",
:children
[{:type "folder",
:empty false,
:name "Rock",
:children
[{:type "folder",
:empty true,
:name "Empty Folder",
:children [{:type "yes", :depth 5, :children []}],
:depth 4}
{:type "file",
:name "Lyrics",
:extension ".txt",
:preview "Curabitur molestie ex ligula, ac tincidunt nisi",
:depth 4,
:children []}],
:depth 3}
{:type "file",
:name "Song Lyrics",
:extension ".txt",
:preview "Nam venenatis nunc eget imperdiet laoreet. Vivamus",
:depth 3,
:children []}],
:depth 2}
{:type "file",
:name "Album Artwork",
:extension ".png",
:preview "photos.google.com/abc.jpg",
:depth 2,
:children []}],
:depth 1}
{:type "file",
:name "Recipe",
:extension ".txt",
:preview " Aliquam bibendum lacus ex",
:depth 1,
:children []}]}
2nd data set:
(add-depth-to-node 0 data-2) =>
{:type "folder",
:empty "false",
:name "My Documents",
:children
[{:type "file",
:name "My Text 1",
:extension ".txt",
:preview
"In ipsum est, faucibus ac tempor vel, tincidunt ac leo. Vestibulum quis odio nisl. Nulla auctor erat a ",
:depth 1,
:children []}
{:type "folder",
:empty "false",
:name "My Music",
:children
[{:type "folder",
:empty "false",
:name "Pop",
:children
[{:type "folder",
:empty "false",
:name "Rock",
:children
[{:type "folder",
:empty "true",
:name "Empty Folder",
:children [],
:depth 4}
{:type "file",
:name "Lyrics",
:extension ".txt",
:preview
"Curabitur molestie ex ligula, ac tincidunt nisi tempor sit amet. Nullam eget varius at.",
:depth 4,
:children []}],
:depth 3}
{:type "file",
:name "Song Lyrics",
:extension ".txt",
:preview
"Nam venenatis nunc eget imperdiet laoreet. Vivamus luctus luctus neque ",
:depth 3,
:children []}],
:depth 2}],
:depth 1}],
:depth 0}
printing the [name depth]
for both data sets:
[name depth] => ["My Documents" 0]
[name depth] => ["My Text" 1]
[name depth] => ["My Music" 1]
[name depth] => ["Pop" 2]
[name depth] => ["Rock" 3]
[name depth] => ["Empty Folder" 4]
[name depth] => [nil 5]
[name depth] => ["Lyrics" 4]
[name depth] => ["Song Lyrics" 3]
[name depth] => ["Album Artwork" 2]
[name depth] => ["Recipe" 1]
[name depth] => ["My Documents" 0]
[name depth] => ["My Text 1" 1]
[name depth] => ["My Music" 1]
[name depth] => ["Pop" 2]
[name depth] => ["Rock" 3]
[name depth] => ["Empty Folder" 4]
[name depth] => ["Lyrics" 4]
[name depth] => ["Song Lyrics" 3]
Upvotes: 1
Reputation: 51501
You want only a single node in the dispatch…
function, but you give a vector of nodes both in the initial call (maybe, don't know what run!
actuall does) and in the recur
. You need to branch out, e. g. by using mapv
or doseq
and not recur
but an actually recursive call.
Also, you put the depth as keyword argument in the lambda list, but call it with the depth as positional parameter in the recur
.
EDIT: What I meant:
(defn dispatch-nodes-to-state [node & {:keys [depth] :or {depth 0}}]
(re-frame/dispatch [:set-nodes (conj {:depth depth} node)])
(if (= (node :type) "folder") ; <-- Here is error #1
(do
(println "It's a folder")
(recur (node :children) (inc depth))) ; <-- Here is error #2
(println "it's a file")))
The error #1 is that in (node :type)
, node
is a vector, not a map. This means that you passed a vector into this function instead of a map.
The error #2 is that in the recur
form, you again pass a vector, and that you give the depth not as a keyword argument, but as second positional argument.
There are two possible remedies: either you accept a vector of nodes, or you pass only single nodes.
Accepting a vector of nodes seems to me mostly straightforward, assuming that the other parts are OK:
(defn dispatch-nodes-to-state [nodes & {:keys [depth] :or {depth 0}}]
(doseq [node nodes]
(re-frame/dispatch [:set-nodes (conj {:depth depth} node)])
(if (= (node :type) "folder")
(do
(println "It's a folder")
(dispatch-nodes-to-state (node :children) :depth (inc depth)))
(println "it's a file"))))
Upvotes: 2