# Dynamic properties to map

**URL:** <https://community.neo4j.com/t/dynamic-properties-to-map/3355>\
**Category:** Procedures & APOC\
**Created:** [November 26, 2018, 5:32pm UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355 "2018-11-26T17:32:02Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 26, 2018, 5:32pm UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/1 "2018-11-26T17:32:02Z")

</div>

I am trying to transform a group of nodes into a map. I am not sure if this is best done with straight Cypher or whether there is an apoc function to help. ... maybe a combination of both?

If I have the following nodes

CREATE (e1:EntityDef{name:"Person"})  
MERGE (e1)-[:HAS\_A]-\>(:Property{name:"givenName",label:"Given Name",length:35})  
MERGE (e1)-[:HAS\_A]-\>(:Property{name:"surname",label:"Surname",length:35})  
MERGE (e1)-[:HAS\_A]-\>(:Property{name:"dob",label:"Date of Birth",format:"dd-mm-yyyy"})  
CREATE (e2:EntityDef{name:"Organisation"})  
MERGE (e2)-[:HAS\_A]-\>(:Property{name:"name",label:"Name",length:40})  
MERGE (e2)-[:HAS\_A]-\>(:Property{name:"established",label:"Established",format:"dd-mm-yyyy"})

I would like to get this map.

=\> {Person:[{name:"givenName",label:"Given Name",length:35},  
{name:"surname",label:"Surname",length:35},  
{name:"dob",label:"Date of Birth",format:"dd-mm-yyyy"}],  
Organisation:[{name:"name",label:"Name",length:40},  
{name:"established",label:"Established",format:"dd-mm-yyyy"}]  
}

Can some one suggest an aproach to this?

I have considered 'apoc.map.fromNodes', but it seems that I need to provide a single property.

---

<div class="post-metadata">

**Author:** ![kevin.urban](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/kevin.urban/32/2302_2.png) [@kevin.urban](https://community.neo4j.com/u/kevin.urban)\
**Post date:** [November 27, 2018, 2:51am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/2 "2018-11-27T02:51:50Z")

</div>

I tried my hand at this for several minutes, but to no avail: couldn't get values stored in variables to be used as map keys... So I googled a bit and came to this blog: [Neo4j: Cypher - Create Cypher map with dynamic keys | Mark Needham](https://markhneedham.com/blog/2017/09/19/neo4j-cypher-create-cypher-map-with-dynamic-keys/)

From there, I was able to formulate this query, which comes pretty dang close to what you want:

```auto
MATCH (p:EntityDef)-[:HAS_A]->(n)
WITH p.name AS dynamicKey, collect(n) AS dynamicValue
RETURN apoc.map.fromValues([dynamicKey, dynamicValue]) AS map

```

The last step is to combine the two maps into one... But I ran out of time at the moment. Good luck! Let me know if you figure out that last step.

---

<div class="post-metadata">

**Author:** ![kevin.urban](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/kevin.urban/32/2302_2.png) [@kevin.urban](https://community.neo4j.com/u/kevin.urban)\
**Post date:** [November 27, 2018, 3:24am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/3 "2018-11-27T03:24:07Z")

</div>

Had a chance to come back and figure it out, and -- glad to say -- figure it out I did!

```auto
MATCH (p:EntityDef)-[:HAS_A]->(n)
WITH p.name AS dynamicKey, collect(n) AS dynamicValue
WITH apoc.map.fromValues([dynamicKey, dynamicValue]) as map
WITH collect(map) as map_list
WITH map_list[0] as first,
     map_list[1] as second
RETURN apoc.map.merge(first, second)

```

Output:

```auto
{
  "Organisation": [
    {
      "format": "dd-mm-yyyy",
      "name": "established",
      "label": "Established"
    },
    {
      "name": "name",
      "length": 40,
      "label": "Name"
    }
  ],
  "Person": [
    {
      "format": "dd-mm-yyyy",
      "name": "dob",
      "label": "Date of Birth"
    },
    {
      "length": 35,
      "name": "surname",
      "label": "Surname"
    },
    {
      "length": 35,
      "name": "givenName",
      "label": "Given Name"
    }
  ]
}

```

---

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 27, 2018, 7:42am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/4 "2018-11-27T07:42:21Z")

</div>

Thanks for your time.

---

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 28, 2018, 8:39am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/6 "2018-11-28T08:39:37Z")

</div>

The approach I ended up taking is

MATCH (d:EntityDef)  
OPTIONAL MATCH (d)-[:HAS\_A]-\>(p:Property)-[:OF]-\>(t:PropertyType)  
WITH d,collect(apoc.map.merge(properties(p),{type:t.name})) as properties  
RETURN collect({name:d.name,properties:properties}) as entityDefs

This query includes a PropertyType that was not in my initial example, without it we would not have needed the merge.

---

<div class="post-metadata">

**Author:** ![andrew\_bowman](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/andrew_bowman/32/73_2.png) [@andrew\_bowman](https://community.neo4j.com/u/andrew_bowman)\
**Post date:** [November 28, 2018, 8:58am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/7 "2018-11-28T08:58:18Z")

</div>

Looks pretty good, here's a few recommendations that use [map projection](https://neo4j.com/docs/cypher-manual/current/syntax/maps/#cypher-map-projection) to make assembling those maps a bit easier:

```auto
MATCH (d:EntityDef)
OPTIONAL MATCH (d)-[:HAS_A]->(p:Property)-[:OF]->(t:PropertyType)
WITH d,collect(p {.*, type:t.name}) as properties
RETURN collect(d {.name, properties}) as entityDefs

```

---

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 28, 2018, 9:03am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/8 "2018-11-28T09:03:57Z")

</div>

@andrew_bowman

"Map Projection" that is what I was looking for! I could not recall the syntax (or the correct term) and so missed it in the cypher ref card.

Thanks a heap.

---

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 28, 2018, 9:59am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/9 "2018-11-28T09:59:51Z")

</div>

@andrew_bowman the feature @kevin.urban solution has that I was particularly after is that d.name is a key in the map. Using map projection we end up with  
{name:"organisation",  
properties:}

rather than  
{organisation:[properties]}

---

<div class="post-metadata">

**Author:** ![andrew\_bowman](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/andrew_bowman/32/73_2.png) [@andrew\_bowman](https://community.neo4j.com/u/andrew_bowman)\
**Post date:** [November 28, 2018, 10:16am UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/10 "2018-11-28T10:16:14Z")

</div>

In that case you'll want to keep `apoc.map.fromValues()` at the very end:

```auto
RETURN collect(apoc.map.fromValues([d.name, properties])) as entityDefs

```

---

<div class="post-metadata">

**Author:** ![kevin.urban](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/kevin.urban/32/2302_2.png) [@kevin.urban](https://community.neo4j.com/u/kevin.urban)\
**Post date:** [November 28, 2018, 7:15pm UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/11 "2018-11-28T19:15:38Z")

</div>

Hi @andrew_bowman -- Still learning here, so I have a question: Wouldn't your solution give back 2 rows of maps instead of just one map? I would test it myself, but not fully sure how @taffyb re-did their data set.

I ask because originally tinkered with some similar approaches, but found I had to use that apoc.map.merge statement to make it come out like requested:

```auto
{Person:[{name:"givenName",label:"Given Name",length:35},
{name:"surname",label:"Surname",length:35},
{name:"dob",label:"Date of Birth",format:"dd-mm-yyyy"}],
Organisation:[{name:"name",label:"Name",length:40},
{name:"established",label:"Established",format:"dd-mm-yyyy"}]
}

```

---

<div class="post-metadata">

**Author:** ![andrew\_bowman](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/andrew_bowman/32/73_2.png) [@andrew\_bowman](https://community.neo4j.com/u/andrew_bowman)\
**Post date:** [November 28, 2018, 7:37pm UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/12 "2018-11-28T19:37:37Z")

</div>

You're partially correct. My solution followed taffyb's approach of returning a collection of maps, so it will return a single row of a list of map values, one element per :EntityDef node.

In order to return back just a single map, instead of a list of maps, then yes you'd need to use `apoc.map.merge()` as in your suggested solution.

---

<div class="post-metadata">

**Author:** ![taffyb](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/taffyb/32/555_2.png) [@taffyb](https://community.neo4j.com/u/taffyb)\
**Post date:** [November 28, 2018, 7:48pm UTC](https://community.neo4j.com/t/dynamic-properties-to-map/3355/13 "2018-11-28T19:48:10Z")

</div>

@kevin.urban

The nodes that are used in the final solution are

CREATE (e1:EntityDef{name:"Person"})  
MERGE (e1)-[:HAS\_A]-\>(p1:Property{name:"givenName",label:"Given Name",length:35})  
MERGE (e1)-[:HAS\_A]-\>(p2:Property{name:"surname",label:"Surname",length:35})  
MERGE (e1)-[:HAS\_A]-\>(p3:Property{name:"dob",label:"Date of Birth",format:"dd-mm-yyyy"})  
CREATE (e2:EntityDef{name:"Organisation"})  
MERGE (e2)-[:HAS\_A]-\>(p4:Property{name:"name",label:"Name",length:40})  
MERGE (e2)-[:HAS\_A]-\>(p5:Property{name:"established",label:"Established",format:"dd-mm-yyyy"})  
CREATE (t1:PropertyType{name:"text"})  
CREATE (t2:PropertyType{name:"date"})  
MERGE (p1)-[:OF]-\>(t1)  
MERGE (p2)-[:OF]-\>(t1)  
MERGE (p3)-[:OF]-\>(t2)  
MERGE (p4)-[:OF]-\>(t1)  
MERGE (p5)-[:OF]-\>(t2)

the final cypher is  
MATCH (d:EntityDef)  
OPTIONAL MATCH (d)-[:HAS\_A]-\>(p:Property)-[:OF]-\>(t:PropertyType)  
WITH d,collect(p {.\*, type:t.name}) as properties  
RETURN apoc.map.mergeList(collect(apoc.map.fromValues([d.name, properties]))) as entityDefs

result:  
{  
"Organisation": [  
{ "format": "dd-mm-yyyy", "name": "established", "label": "Established", "type": "date" },  
{ "name": "name", "length": 40, "label": "Name", "type": "text" }  
],  
"Person": [  
{ "format": "dd-mm-yyyy", "name": "dob", "label": "Date of Birth", "type": "date" },  
{ "length": 35, "name": "surname", "label": "Surname", "type": "text" },  
{ "length": 35, "name": "givenName", "label": "Given Name", "type": "text" }  
]  
}
