I am new to Clojure and I am trying to iterate over a PersistentVector of objects, which looks like below when I print in logs.
[{name John, email john#example.com} {name Peter, email peter#example.com}]
when I print (type myData) it tells me it is of type clojure.lang.PersistentVector
I have to iterate over this vector, extract email from each object inside it at a time and invoke another function.
It looks very simple, I looked at many samples and questions, but nothing seem to work for me :(
Thanks.
Here are a couple of options:
If you don't care about the objects ("maps" in Clojure speak) and only want to collect the emails and apply a function over them, you can create a simple function just to extract the emails into another collection and then use any function over the collection of emails:
(def items
[{"name" "John", "email" "john#example.com"} {"name" "Peter", "email" "peter#example.com"}])
(defn get-email [m]
(get m "email"))
(mapv get-email items)
;; returns: ["john#example.com", "peter#example.com"]
You can use function composition to the create a function that turns into upper case (using upper-case from clojure.string) the result of getting the email: (note the ordering in comp):
(mapv (comp clojure.string/upper-case get-email) items)
;; returns ["JOHN#EXAMPLE.COM" "PETER#EXAMPLE.COM"]
If you want to obtain a similar collection but with the email field updated, you can use update-in:
(mapv (fn [m] (update-in m ["email"] clojure.string/upper-case)) items)
;; Applies `upper-case` to the "email" path of each map identified by `m`.
;; returns: [{"name" "John", "email" "JOHN#EXAMPLE.COM"} {"name" "Peter", "email" "PETER#EXAMPLE.COM"}]
There are more elegant ways to do the same work using other techniques (anonymous functions, strs destructuring) but I think the ones above are simpler to understand if you are new to Clojure.
Related
I have data in clojure defined as:
(def data {:actor "damas.nugroho"
:current-attributes [{:key "name", :value "adam"}
{:key "city", :value "jakarta"}]})
and I want to get the city value which means jakarta. How can I do that?
This is the data you have:
(def data
{:actor "damas.nugroho"
:current-attributes [{:key "name", :value "adam"}
{:key "city", :value "jakarta"}]})
And this is how you get at the city:
(->> (:current-attributes data)
(some #(when (= (:key %) "city")
(:value %))))
But the value of :current-attributes seems like a map, by essence. Assuming the keys don't repeat, consider transforming it into a map for easier manipulation.
(def my-new-data
(update data :current-attributes
#(into {} (map (juxt :key :value)) %)))
my-new-data will end up becoming this:
{:actor "damas.nugroho"
:current-attributes {"name" "adam"
"city" "jakarta"}}
Getting the city is then just a nested map lookup, and can be done with (get-in my-new-data [:current-attributes "city"])
Also, :current-attributes is not specific and unless it may contain :actor as a key, or has a particular meaning you care about in some context, can be flattened.
Also, assuming the names of the keys in current-attributes are programmatic names, and follow the syntax of keywords in Clojure, you could convert them to keywords, and pour them all into a map:
(def my-newer-data
(let [attrs (into data
(map (juxt (comp keyword :key) :value))
(:current-attributes data))]
(dissoc attrs :current-attributes)))
Now we end up with this:
{:actor "damas.nugroho", :name "adam", :city "jakarta"}
And extracting city is just saying it, (:city my-newer-data)
We need to perform a phrased-based search (like Google's "") over a nested array of key words, by order.
For instance, let us suppose the data looks like:
{
Name: "question",
body: [
"We",
"need",
"to",
"perform",
"a",
"search",
"like",
"google's"
]
}
By searching: "we search" – I will get no result, but the document will be returned by searching any of the followings: "we need", "to perform a search", "we" etc.
I do need to tokenize the words for encryption, so saving them as a string could not do for me here…
Is that any possible?
Folks, I tried to solve it with the technical support of MongoDB. Apparently, there is not out-of-the-box solution.
I have been able to "solve" this by keeping another field, concatenating all the tokenized, encrypted words in one string, and use regex expression over it.
Not ideal, and required to duplicate some data – but it works foe our needs.
I have a series of arrays, filled with objects - in JSON format. They look something like this:
Group 1
[
{ "name" : "John",
"age" : "31"
},
{ "name" : "Bob",
"age" : "33"
}
]
Group 2
[
{ "name" : "Jim",
"age" : "46"
},
{ "name" : "Harry",
"age" : "23"
}
] // ... and so on ...
In Angular, how can I join the two arrays to form an array of arrays? I'm guessing it's group1.concat(group2), or something like that? I'm not sure where to do it though, would I do this in the controller?
Currently I have a $scope property assigned to each variable, would I make a new $scope property that was a concatenated array of each of these?
And would that be something like:
$scope.allGroups = []
$scope.allGroups = $scope.group1.concat($scope.group2)
// since 'allGroups', 'group1', and 'group2' have all been defined can I do...
allGroups = group1.concat(group2) // ...or does $scope need to be used each time?
My intention is (with the necessary filters) to be able to do an ng-repeat through all groups as they will now all be linked to one $scope variable.
I'm pretty sure that's laiden with errors, but I thought it better to provide some bad code than nothing at all, just so it was more evident what I was trying to do. If there are better approaches (which I'm sure there are), I'm all ears.
Thanks in advance
You're right, array1.concat(array2) is the good method to use.
Now the question is, do you need group1 and group2 to be on your $scope ? Do you need to display them ?
If the answer is no, then you could simply do as follow:
Recover the two arrays and store them in 2 "private" variables
Concat them into a variable set into your $scope
You dont have to set variable into your $scope if you dont display them. It will then look like this:
$scope.allGroups = group1.concat(group2)
Otherwise, no other choice than do like you said:
$scope.allGroups = $scope.group1.concat($scope.group2)
EDIT
If you want an array containing the group1 and group2 arrays, and not only their content, you can simply use the push() method as follow:
$scope.allGroups = [];
$scope.allGroups.push(group1, group2);
If you want to be able to access the concatenated array from your views you have to attach the concatenated array in the $scope object, so you will have to use
$scope.allGroups = $scope.group1.concat($scope.group2)
In the case that you leave var allGroups not attached to the $scope object allGroups will be a local variable to the controller function and will be available only through a closure
You can use concat() to join one array with another.
concat() function returns an array.
Here is the code:
$scope.a = [1,2];
$scope.b = [3,4];
$scope.c = $scope.a.concat($scope.b);
What is the most efficient way to break down this CREATE cypher query?
The end pattern is the following:
(newTerm:term)-[:HAS_META]->(metaNode:termMeta)
In this pattern this is a single newTerm node and about ~25 termMeta nodes. The HAS_META relationship will have a single property (languageCode) that will be different for each termMeta node.
In the application, all of these nodes and relationships will be created at the same time. I'm trying to determine the best way to add them.
Is there anyway to add these without having to have perform individual query for each TermMeta node?
I know you can add multiple instances of a node using the following query format:
"metaProps" : [
{"languageCode" : "en", "name" : "1", "dateAdded": "someDate1"},
{"languageCode" : "de", "name" : "2", "dateAdded": "someDate2"},
{"languageCode" : "es", "name" : "3", "dateAdded": "someDate3"},
{"languageCode" : "fr", "name" : "3", "dateAdded": "someDate4"}
]
But you can only do that for one type of node at a time and there (as far as I can tell) is no way to dynamically add the relationship properties that are needed.
Any insight would be appreciated.
There's no really elegant way to do it, as far as I can tell—from your example, I'm assuming you're using parameters. You can use a foreach to loop through the params and do a create on each one, but it's pretty ugly, and requires you to explicitly specify literal maps of your properties. Here's what it would look like for your example:
CREATE (newTerm:term)
FOREACH ( props IN {metaProps} |
CREATE newTerm-[:HAS_META {languageCode: props.languageCode}]->
(:termMeta {name: props.name, dateAdded: props.dateAdded})
)
WITH newTerm
MATCH newTerm-[rel:HAS_META]->(metaNode:termMeta)
RETURN newTerm, rel, metaNode
If you don't need to return the results, you can delete everything after the FOREACH.
Select and name each vertex differently and then create relations using it.
For ex
match (n:Tag), (m:Account), (l:FOO) CREATE (n)-[r:mn]->(m),(m)-[x:ml]->(l)
match (n:Tag{a:"a"}), (m:Account{b:"x"}), (l:FOO) CREATE (n)-[r:mn]->(m),(m)-[x:ml]->(l)
I am calling data that includes a nested array.
Example:
data = [
'name':'John', 'likes': ['Women', 'Bars', 'Women_In_Bars', 'turtles'],
'name': 'Steve', 'likes': ['Men', 'Clubs', 'Men_In_Clubs', 'cats']
]
I am already sorting by name in my queryset, and the Resource is already mapped correctly to the other likeResource as per the docs.
How can I also return the nested likes in a sorted order?
It's really difficult to say if you don't share the code where you are creating each resource, etc...
I guess you could try using the dehydrate function:
def dehydrate(self, bundle):
#bundle.data['data'] might contain the array you mention above
for item in bundle.data['data']:
sort_function(item['likes'])
Hope it helps!