I need to add key value pair at the end of the tree like json object.
[{
"name": "minpur",
"children": [{
"name": "ppp1",
"children": [{
"name": "feeder",
"children": [{
"name": "rmu16",
"children": [{
"name": "invt16",
"children": [{
"aname": "inv 01"
}]
}]
}]
}]
}]
}]
Expected
[{
"name": "minpur",
"children": [{
"name": "ppp1",
"children": [{
"name": "feeder",
"children": [{
"name": "rmu16",
"children": [{
"name": "invt16",
"children": [{
"aname": "inv 01",
**
"value": 300 **
}]
}]
}]
}]
}]
}]
tried recursive function as below
let traverse = function(jsonObj) {
if (jsonObj !== null && typeof jsonObj == "object") {
return Object.entries(jsonObj).forEach(([key, value]) => {
if (key != "aname") {
traverse(value);
} else {
return value;
}
});
}
}
Check if the aname key exists on the object, and add the property if it does. If it doesn't iterate the children with Array.forEach(), and calls traverse on the children.
const traverse = (key, value) => obj => {
const inner = obj => {
if(obj.hasOwnProperty(key)) obj.value = value
else obj.children.forEach(inner)
}
return inner(obj)
}
const tree = [{"name":"minpur","children":[{"name":"ppp1","children":[{"name":"feeder","children":[{"name":"rmu16","children":[{"name":"invt16","children":[{"aname":"inv 01"}]}]}]}]}]}]
tree.forEach(traverse('aname', 300))
console.log(tree)
Here is a more functional solution where the original values are not changed.
The children are inspected and if there is no children, the passed keys are used to extend the current node.
const obj = [{
"name": "minpur",
"children": [{
"name": "ppp1",
"children": [{
"name": "feeder",
"children": [{
"name": "rmu16",
"children": [{
"name": "invt16",
"children": [{
"aname": "inv 01"
}]
}]
}]
}]
}]
}]
function addToLeaf(append, node) {
if (!node.children) {
return {
...node,
...append
}
}
return {
...node,
...{ children: node.children.map(child => addToLeaf(append, child))}
}
}
const result = obj.map((node) => addToLeaf({
value: 300
}, node));
console.log(result);
If you want to use JS utility library lodash it gives you many methods to handle such complex structures.
For this I would suggest _.set - https://lodash.com/docs/4.17.11#set
const _u = _.noConflict();
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_u.set(object, 'a[0].b.c', 4);
console.log(object);
// => 4
_u.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object);
// => 5
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You can create a recursive function which traverses arrays of objects and merges a found object (by specified key name) with a specified by you argument. No lodash needed:
let data = [{ "name": "minpur", "children": [{ "name": "ppp1", "children": [{ "name": "feeder", "children": [{ "name": "rmu16", "children": [{ "name": "invt16", "children": [{ "aname": "inv 01" }] }] }] }] }] }]
let deepSetByKey = (a, k, setObj) =>
a.forEach(o => k in o ? Object.assign(o, setObj) :
'children' in o ? deepSetByKey(o.children, k, setObj) : 0) && a
deepSetByKey(data, 'aname', { value: 300 }) // call the fn
console.log(data[0].children[0].children[0].children[0].children[0].children[0])
The idea is to recursively traverse with Array.forEach and use Object.assign when you find the prop you are looking for in the current object.
Related
I am trying to reformat some relatively unstructured json to fit my needs, where I want each object to have a 'name' attribute as well as a 'children' array for sub-objects. I have a json file that looks like below:
{
"varA":true,
"varB":false,
"varC": {
"time":"15:00:00",
"date":"Jul 10",
"items":["apple", banana"]
}
}
I would like to format it to be something like this:
{
"name": "data",
"children": [
{
"name": "varA",
"children": [
{"name": "true"}
]
},
{
"name": "varB",
"children": [
{"name": "false"}
]
},
{
"name": "varC",
"children": [
{
"name": "time",
"children": [
{"name": "15:00:00"}
]
},
{
"name": "date",
"children": [
{"name": "Jul 10"}
]
},
{
"name": "items",
"children": [
{"name": "apple"},
{"name": "banana"}
]
}
]
}
]
}
How would I be able to do this recursively using pure js, in the case that objects can have a different depths of subobjects? Or is it still possible to do it iteratively?
Thanks in advance!!!
const data = {
"varA":true,
"varB":false,
"varC": {
"time":"15:00:00",
"date":"Jul 10",
"items":["apple", "banana"]
}
}
function parse(key, val) {
if (val === undefined) return { name: key };
if (typeof val !== "object") {
return {
name: key,
children: [
parse(val)
]
}
}
return {
name: key,
children: Object.entries(val).map(([newKey, newVal]) => parse(newKey, newVal))
}
}
const parsedData = parse("data", data)
{
"arr1":[
{
"name":"something1",
"id":"233111f4-9126-490d-a78b-1724009fa484"
},
{
"name":"something2",
"id":"50584c03-ac71-4225-9c6a-d12bcc542951"
},
{
"name":"Unique",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"
},
{
"name":"something4",
"id":"ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7"
},
{
"name":"something5",
"id":"ef825dc3-003c-4740-955a-bb437cfb4199"
}
],
"arr2":
[
{
"name":"Unique",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"}
]
}
This is list of arrays with keys and values as array, I want to return all the keys based on a particular value;
For Eg:
I want to return the parent keys which are [arr1,arr2], reason being both the arrays contain a value Unique, So I want to return the parent key of both the values, which is arr1 and arr2 respectively.
Note: The list can have n numbers of arrays.
Any help would be appreciated. Thanks in advance.
The simplest way to go about this is:
Loop through the keys in your object
Check if the array contains any objects with the name "Unique"
If so, add the objects key to an array
const obj = {
"arr1": [{ "name": "something1", "id": "233111f4-9126-490d-a78b-1724009fa484" }, { "name": "something2", "id": "50584c03-ac71-4225-9c6a-d12bcc542951" }, { "name": "Unique", "id": "43cf14ee58ea4d8da43e9a2f208d215c" }, { "name": "something4", "id": "ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7" }, { "name": "something5", "id": "ef825dc3-003c-4740-955a-bb437cfb4199" }],
"arr2": [{ "name": "Unique", "id": "43cf14ee58ea4d8da43e9a2f208d215c" }],
"arr3": [{ "name": "No unique here","id": "Example" }]
}
// Create our array that will contain the keys
const keys = []
// Loop through each key in the object
for (const prop in obj) {
// Use .some to see if any of the objects in this array have the selected name
const containsUnique = obj[prop].some(o => o.name === 'Unique')
if (containsUnique) {
// Add the current key to the array
keys.push(prop)
}
}
// Use the array of keys which contain an object named "Unique"
console.log(keys)
This is a more generic approach:
const getKeysByValue = (data, value) => {
const dataKeys = Object.keys(data);
const valueKey = Object.keys(value);
return dataKeys.filter(currKey => {
for(let element of data[currKey])
if(element[valueKey] === value[valueKey])
return true;
});
}
const data = {
"arr1":[
{
"name":"something1",
"shape": "Trapezium",
"id":"233111f4-9126-490d-a78b-1724009fa484"
},
{
"name":"something2",
"shape": "Octagon",
"id":"50584c03-ac71-4225-9c6a-d12bcc542951"
},
{
"name":"Unique",
"shape": "Square",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"
},
{
"name":"something4",
"shape": "Triangle",
"id":"ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7"
},
{
"name":"something5",
"shape": "Circle",
"id":"ef825dc3-003c-4740-955a-bb437cfb4199"
}
],
"arr2":
[
{
"name":"Unique",
"shape": "Triangle",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"
}
],
"arr3":
[
{
"name":"Not-Unique",
"shape": "Circle",
"id":"8hcf14ee58ea25g343e9a2f208df215c"
}
]
}
console.log(getKeysByValue(data, {"name": "something2"})); // ["arr1"]
console.log(getKeysByValue(data, {"name": "Unique"})); // ["arr1", "arr2"]
console.log(getKeysByValue(data, {"shape": "Circle"})); // ["arr1", "arr3"]
console.log(getKeysByValue(data, {"shape": "Square"})); // ["arr1"]
The function receives two parameters, data and value. value is expected to be in the format of the value you are looking to filter with. In your example you wanted it to be "Unique" and in each object in the array it was presented like "name": "Unique" so we will send it as an object, {"name": "Unique"}.
In this way you can have different value to filter with. In the example above I added a shape key and value to each element, we can filter by this value too as shown in the example above.
you can do like this :
const obj = {
"arr1": [{ "name": "something1", "id": "233111f4-9126-490d-a78b-1724009fa484" }, { "name": "something2", "id": "50584c03-ac71-4225-9c6a-d12bcc542951" }, { "name": "Unique", "id": "43cf14ee58ea4d8da43e9a2f208d215c" }, { "name": "something4", "id": "ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7" }, { "name": "something5", "id": "ef825dc3-003c-4740-955a-bb437cfb4199" }],
"arr2": [{ "name": "Unique", "id": "43cf14ee58ea4d8da43e9a2f208d215c" }],
"arr3": [{ "name": "No unique here","id": "Example" }]
}
arr=[]
//loop over dict with pair keys and value
for (const [key, value] of Object.entries(obj)) {
//get the list of name from dict and check it if it contains Unique string
value.map(e=>e.name).includes("Unique") ? arr.push(key) : false
}
console.log(arr)
You can use array some method
const data = {
"arr1": [{
"name": "something1",
"id": "233111f4-9126-490d-a78b-1724009fa484"
},
{
"name": "something2",
"id": "50584c03-ac71-4225-9c6a-d12bcc542951"
},
{
"name": "Unique",
"id": "43cf14ee58ea4d8da43e9a2f208d215c"
},
{
"name": "something4",
"id": "ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7"
},
{
"name": "something5",
"id": "ef825dc3-003c-4740-955a-bb437cfb4199"
}
],
"arr2": [{
"name": "Unique",
"id": "43cf14ee58ea4d8da43e9a2f208d215c"
}]
}
var obj = [],
keys;
for (keys in data) {
data[keys].some(a => "Unique" === a.name) && obj.push(keys);
}
console.log(obj);
An alternative way that i could think of is using Regexp
var obj = {
"arr1":[
{
"name":"something1",
"id":"233111f4-9126-490d-a78b-1724009fa484"
},
{
"name":"something2",
"id":"50584c03-ac71-4225-9c6a-d12bcc542951"
},
{
"name":"Unique",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"
},
{
"name":"something4",
"id":"ce0374ba-6d9b-4ff5-98b1-1191d1d2a9a7"
},
{
"name":"something5",
"id":"ef825dc3-003c-4740-955a-bb437cfb4199"
}
],
"arr2":
[
{
"name":"Unique",
"id":"43cf14ee58ea4d8da43e9a2f208d215c"}
]
}
let str = JSON.stringify(obj);
let match = str.matchAll(/\"([\w\d]+)\":\[(?:{[\s\S]+},)*{\"name\":\"Unique\"/g);
let parent = [];
for(let m of match){
parent.push(m[1]);
}
This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 1 year ago.
can you help me with this problem with js\react?
I'm trying to manage 2 arrays due to obtain a new object based on their shared attribute (Array A: "id" and Array B: "parent")
I think isn't hard but I'm struggling to do it atm :(
Array A
[{
"id": "606f1a2bebb5fb53804dd3d5",
"name": "cc",
}, {
"id": "606f1a30cfe84430c41dce88",
"name": "bb",
}, {
"id": "606f1a4ed2ff554e4ea11b82",
"name": "ff",
}]
Array B
[{
"id": "3344",
"color": "pink",
"parent": "606f1a2bebb5fb53804dd3d5",
}, {
"id": "3453",
"color": "blue",
"parent": "606f1a30cfe84430c41dce88",
}, {
"id": "3331",
"color": "yellow",
"parent": "606f1a4ed2ff554e4ea11b82",
}, {
"id": "4442",
"color": "black",
"parent": "606f1a30cfe84430c41dce88",
}]
I want merge these two arrays and create a new one where the array B objects are split by "id" of array A.
Something like this:
[{
"606f1a2bebb5fb53804dd3d5": [{
"id": "3344",
"color": "pink",
"parent": "606f1a2bebb5fb53804dd3d5",
}]
}, {
"606f1a30cfe84430c41dce88": [{
"id": "3453",
"color": "blue",
"parent": "606f1a30cfe84430c41dce88",
}, {
"id": "4442",
"color": "black",
"parent": "606f1a30cfe84430c41dce88",
}]
}, {
"606f1a4ed2ff554e4ea11b82": [{
"id": "3331",
"color": "yellow",
"parent": "606f1a4ed2ff554e4ea11b82",
}]
}]
Thanks very much guys
You just need array b for grouping and another object for keeping track of the max key inside of a group.
const
data = [{ id: "3344", color: "pink", parent: "606f1a2bebb5fb53804dd3d5" }, { id: "3453", color: "blue", parent: "606f1a30cfe84430c41dce88" }, { id: "3331", color: "yellow", parent: "606f1a4ed2ff554e4ea11b82" }, { id: "4442", color: "black", parent: "606f1a30cfe84430c41dce88" }],
max = {},
result = data.reduce((r, o) => {
max[o.parent] = (max[o.parent] || 0) + 1;
(r[o.parent] ??= {})[max[o.parent]] = o;
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think you can loop through the items and then find the parent
let arrayC = [];
arrayB.forEach(item => {
let parent = array1.find(e => e.id === item.parent);
// do something here to combine it into one object
// then arrayC.push(newItem);
});
https://codesandbox.io/s/boring-snowflake-brksj?file=/src/index.js
const result = arrayA.reduce((acc, arrayAItem) => {
return {
...acc,
[arrayAItem.id]: arrayB.filter(
(arrayBItem) => arrayBItem.parent === arrayAItem.id
)
};
}, {});
}]
You can do this with map and filter functions of array.
const newArray = array1.map(array1Item => {
return { [array1Item.id]: array2.filter(array2Item => array2Item.parent === array1Item.id)}
})
Based on the two arrays that you have, I assume that they are only linked by their "parent" ids. If this is the case, you can reduce the B array using the A array as an accumulator.
const a = [
{ "id": "606f1a2bebb5fb53804dd3d5" , "name": "cc" },
{ "id": "606f1a30cfe84430c41dce88" , "name": "bb" },
{ "id": "606f1a4ed2ff554e4ea11b82" , "name": "ff" },
];
const b = [
{ "id": "3344" , "color": "pink" , "parent": "606f1a2bebb5fb53804dd3d5" },
{ "id": "3453" , "color": "blue" , "parent": "606f1a30cfe84430c41dce88" },
{ "id": "3331" , "color": "yellow" , "parent": "606f1a4ed2ff554e4ea11b82" },
{ "id": "4442" , "color": "black" , "parent": "606f1a30cfe84430c41dce88" },
];
const c = Object
.entries(b.reduce((acc, { id, color, parent }) =>
({ ...acc, [parent]: {
...acc[parent],
colors: [...acc[parent].colors, { id, color } ]
}}),
Object.fromEntries(a.map(({ id, name }) =>
[ id, { name, colors: [] } ]))))
.map(([ id, value ]) => value);
console.log(c);
.as-console-wrapper { top: 0; max-height: 100% !important; }
I would like to obtain the value of name using the code below.
When find(z => z.key.value === 123) be added to the code ,there will be this error :
x.obj.array_1.find(...).find is not a function
What's wrong with this code :
const data = [{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "123"
}
}]
}]
}]
},
"name": "obj1"
},
{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "456"
}
}]
}]
}]
},
"name": "obj2"
},
{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "789"
}
}]
}]
}]
},
"name": "obj3"
}
]
const name = data
.filter(x =>
x.obj.array_1.find(y => y.array_2).find(y => y.array_3).find(z => z.key.value === 123)
).map(x => x.name);
console.log(name)
Small fix here, this work provided that your real life data is also in this shape
After finding the propriate array_1 element that have array2[0] which has non-nil array_3, you have to access that array_3 to iterate and find the expected value
Edited (because array_2 and array_3 may have multiple elements): you have to extract all array_3 element (using flatMap to flatten the nested array) and do the condition check from that extracted array
const name = data
.filter((x) => {
const flattened_array_3 = x.obj.array_1.flatMap((array_1_el) =>
array_1_el.array_2.flatMap((array_2_el) => array_2_el.array_3)
)
return flattened_array_3.find((array_3_el) => +array_3_el.key.value === 123)
})
.map((x) => x.name)
Full code
const data = [
{
obj: {
array_1: [
{
array_2: [
{
array_3: [
{
key: {
value: "123",
},
},
],
},
],
},
],
},
name: "obj1",
},
{
obj: {
array_1: [
{
array_2: [
{
array_3: [
{
key: {
value: "456",
},
},
],
},
],
},
],
},
name: "obj2",
},
{
obj: {
array_1: [
{
array_2: [
{
array_3: [
{
key: {
value: "789",
},
},
],
},
],
},
],
},
name: "obj3",
},
]
const name = data
.filter((x) => {
const flattened_array_3 = x.obj.array_1.flatMap((array_1_el) =>
array_1_el.array_2.flatMap((array_2_el) => array_2_el.array_3)
)
return flattened_array_3.find((array_3_el) => +array_3_el.key.value === 123)
})
.map((x) => x.name)
console.log(name)
Reference: flatMap
x.obj.array_1.find(y => y.array_2)
is undefined.
Try to split your code
In order to work on this structure, I advise you to change the way you represent data.
It seems like some kind of output of tokenization. There have to bee tools to extract information from the path for example XPath, etc.
On the other hand below code should work for your case
The key point is using flatMap to rest inner arrays to upper one
const data = [{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "123"
}
}]
}]
}]
},
"name": "obj1"
},
{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "456"
}
}]
}]
}]
},
"name": "obj2"
},
{
"obj": {
"array_1": [{
"array_2": [{
"array_3": [{
"key": {
"value": "789"
}
}]
}]
}]
},
"name": "obj3"
}
]
var obj = data.find(x=> x.obj.array_1.flatMap(x=>x.array_2).flatMap(x=>x.array_3)[0].key.value==789)
console.log(obj.name)
I got stuck on a maybe simple task, but could not find any solution.
I have some JSON Data - lets say:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"]
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"]
}, {
"_id": 3,
"type": "department",
"Name": "Marketing"
}, {
"_id": 4,
"type": "department",
"Name": "Sales"
}]
As I learned here it is quite simple to get all the persons and the departments they work for together using a map array for the departments.
Then I can map the corresponding department to the Person and receive something like:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
"Readable": ["Marketing", "Sales"]
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
"Readable": ["Sales"]
}]
But for another interface I need the data "the other way round" e.g.
[{
"_id": 3,
"type": "department",
"Name": "Marketing",
"employees": [
"Hans", "Michael"
]
}, {
"_id": 4,
"type": "department",
"Name": "Sales",
"employees": [
"Hans"
]
}]
Is there any decent way to achieve this structure? Two days of trying didn't get me anywhere...
var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"] }, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"] }, { "_id": 3, "type": "department", "Name": "Marketing" }, { "_id": 4, "type": "department", "Name": "Sales" }];
var departments = [],
persons = [];
data.forEach(e => {
if (e.type === "person") {
persons.push(e);
} else if (e.type === "department") {
departments.push(e);
e.employees = [];
}
});
departments.forEach(d => {
var workers = persons.filter(p => p.WorksFor.indexOf(d._id.toString()) > -1)
/*.map(p => p.Name)*/ // add this if you only need the name instead of the complete "person"
d.employees = d.employees.concat(workers);
});
console.log(JSON.stringify(departments, null, 4));
You can try something like this:
var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}]
var ignoreDept = ['person'];
var result = data.reduce(function(p,c,i,a){
if(ignoreDept.indexOf(c.type) < 0){
c.employees = a.reduce(function(arr,emp){
if(emp.WorksFor && emp.WorksFor.indexOf(c._id.toString()) > -1){
arr.push(emp.Name)
}
return arr;
},[]);
p.push(c);
}
return p;
}, []);
console.log(result)
The solution using Array.prototype.filter() and Array.prototype.forEach() functions:
var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}],
// getting separated "lists" of departments and employees(persons)
deps = data.filter(function(o){ return o.type === "department"; }),
persons = data.filter(function(o){ return o.type === "person"; });
deps.forEach(function (d) {
d['employees'] = d['employees'] || [];
persons.forEach(function (p) {
if (p.WorksFor.indexOf(String(d._id)) !== -1) { // check the `id` coincidence between the employee and the department
d['employees'].push(p.Name);
}
});
});
console.log(deps);
You could use a hash table and a single loop for each array.
Methods:
Array#reduce for iterating an array and returning the result,
Array#forEach for looping the inner array WorksFor,
Object.create(null) to generate an object without any prototypes,
some other pattern, like a closure over hash and
the use of logical OR || for checking a falsy value and taking an object as default.
hash[b] = hash[b] || { _id: b, employees: [] };
var data = [{ _id: 1, type: "person", Name: "Hans", WorksFor: [3, 4] }, { _id: 2, type: "person", Name: "Michael", WorksFor: [3] }, { _id: 3, type: "department", Name: "Marketing" }, { _id: 4, type: "department", Name: "Sales" }],
result = data.reduce(function (hash) {
return function (r, a) {
if (a.type === 'person') {
a.WorksFor.forEach(function (b) {
hash[b] = hash[b] || { _id: b, employees: [] };
hash[b].employees.push(a.Name);
});
}
if (a.type === 'department') {
hash[a._id] = hash[a._id] || { _id: b, employees: [] };
hash[a._id].type = a.type;
hash[a._id].Name = a.Name;
r.push(hash[a._id]);
}
return r;
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a way you can get the first mapping. I've added some comments so you can follow along, and with it I hope you can find the answer to your second problem.
// First, let's get just the items in this array that identify persons
// I've called this array "data"
data.filter(x => x.type === 'person')
// Now let's map over them
.map(person =>
// We want all of the data associated with this person, so let's
// use Object.assign to duplicate that data for us
Object.assign({}, person, {
// In addition, we want to map the ID of the WorksFor array to the Name
// of the corresponding department. Assuming that the _id key is unique,
// we can due this simply by mapping over the WorksFor array and finding
// those values within the original array.
Readable: person.WorksFor.map(wfId =>
// Notice here the parseInt. This will not work without it due to
// the type difference between WorksFor (string) and _id (integer)
data.find(d => d._id === parseInt(wfId)).Name
)
})
);
var data = [{ "_id": 1, "type": "person", "Name": "Hans", "WorksFor": ["3", "4"]}, { "_id": 2, "type": "person", "Name": "Michael", "WorksFor": ["3"]}, { "_id": 3, "type": "department", "Name": "Marketing"}, { "_id": 4, "type": "department", "Name": "Sales"}];
var dep = {};
data.forEach(e => (e.type === 'person' && e.WorksFor.forEach(d => dep[d]? dep[d].push(e.Name): dep[d] = [e.Name])));
data.forEach(e => (e.type == 'department' && (e.employees = dep[e._id] || [])));
data = data.filter(e => e.type == 'department');
console.log(data);