Given the following sample JSON (stringified from the corresponding JavaScript object), I need to extract this information:
Find the object in persons which has the reference = 2.
If a person with this reference was found, get the name of the person's parent element (here: "B").
In the end, I need to build a new object looking similar to this. This won't be problematic but I'm struggling with how to extract these objects from the source. I tried different approaches with find(), map(), flatMap() and filter() but none of them really worked.
{
companyName: "B",
person: {
"reference": 2,
"name": "Bob"
}
}
Source
{
"root": [
{
"companies": [
{
"name": "A",
"persons": [
{
"reference": 1,
"name": "Alex"
}
]
}
]
},
{
"companies": [
{
"name": "B",
"persons": [
{
"reference": 2,
"name": "Bob"
},
{
"reference": 3,
"name": "Charles"
}
]
}
]
}
]
}
If you're just interested in the name of the company you can find it using:
const reference = 2;
const company = data.root.flatMap(item => item.companies)
.find(company => company.persons.some(person => person.reference === reference));
const companyName = company?.name;
// or if you cannot use optional chaining
const companyName = (company || {}).name;
In data.root.flatMap(item => item.companies) we iterate through all items in root, for each item we select its companies property. Since we don't want a nested array we use flatMap() to flatten the result by 1 level. This leaves us with an array of companies.
After that we'll call find() on the companies array, since we are looking for a specific company name. The criteria of the company is that some() (1 or more) of the persons should match the provided reference. If no match is found null will be returned (from find()).
We then take the find() result (company) and navigate to the name via optional chaining ?.. This will return the name of the company if present, or undefined if company is null
You can use array.reduce here
let data = JSON.parse(`{
"root": [
{
"companies": [
{
"name": "A",
"persons": [
{
"reference": 1,
"name": "Alex"
}
]
}
]
},
{
"companies": [
{
"name": "B",
"persons": [
{
"reference": 2,
"name": "Bob"
},
{
"reference": 3,
"name": "Charles"
}
]
}
]
}
]
}`)
// GET ALL THE COMPANIES DATA
let companies = data.root.reduce(
(prevValue, currValue) => {
prevValue.push(...currValue.companies)
return prevValue
},
[]
)
// FIND AND CREATE EXPECTED RESULT SET
let results = companies.reduce(
(prevValue, currValue) => {
// loop inside a loop, mind it
let refPerson = currValue.persons.find((item)=> item.reference == 2)
if(refPerson){
prevValue.push({
companyName: currValue.name,
person: refPerson
})
}
return prevValue
},
[]
)
console.log(results)
I have below array. First object is the original data. Inside array is the changed value. I am trying to create a final data by matching with the Name field with inside array. which should look like
var a =
[
{"Id":"1","Test":"Name1","Name":"hunt9988ggggggggggggdfsf1111"},
{"Id":"2","Test":"Name2","Name":"hunt9988ggggggggggggdfsf"},
[
**{"Name":"hunt9988ggggggggggggdfsf1118","Id":"1"}, // Changed value
{"Name":"hunt9988ggggggggggggdfsf1118","Id":"2"}**
]
]
Final Data
var a =
[
{"Id":"1","Test":"Name1","Name":"hunt9988ggggggggggggdfsf1118"},
{"Id":"2","Test":"Name2","Name":"hunt9988ggggggggggggdfsf1118"}
]
I am trying with below code
var result = a.map(item => ({ value: item.Id, text: item.Name}));
console.log(result)
Like this?
Note I modify the original array
let a = [{
"Id": "1",
"Test": "Name1",
"Name": "hunt9988ggggggggggggdfsf1111"
},
{
"Id": "2",
"Test": "Name2",
"Name": "hunt9988ggggggggggggdfsf"
},
[{
"Name": "hunt9988ggggggggggggdfsf1118",
"Id": "1"
}, // Changed value
{
"Name": "hunt9988ggggggggggggdfsf1118",
"Id": "2"
}
]
]
const replaceArray = a.find(item => Array.isArray(item))
replaceArray.forEach(item => a.find(aItem => aItem.Id === item.Id).Name=item.Name)
a = a.filter(item => item.Id)
console.log(a)
How can we push values to an object from inside a map function and return that single object. I have string comparison condition inside the map function. I tried using Object.assign but it returns an array with multiple object inside that array. Instead of this multiple object I'm expecting a single object inside an array.
Map function
let arrayObj = arrayToTraverse.map(function(item) {
var myObj = {};
if(item.inputvalue === 'Name'){
Object.assign(myObj, {name: item.value});
} else if (item.inputvalue === 'Email'){
Object.assign(organizerInfo, {email: item.value});
} else if (item.inputvalue === 'Company'){
Object.assign(organizerInfo, {company: item.value});
}
return myObj;
});
console.log("The array object is", arrayObj)
This return the array of objects as follows
[
{
"name": "Tom"
},
{
"email": "tom#abc.com"
},
{
"company": "ABC"
}
]
But The array I'm expecting is
[
{
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
// or
[
"returned": {
"name": "Tom",
"email": "tom#abc.com",
"company": "ABC"
}
]
An example of arrayToTraverse can be considered as following
[
{
"id": "1",
"inputvalue": "Name",
"value": "Tom",
"type": "Short Text"
},
{
"id": "2",
"inputvalue": "Email",
"value": "tom#abc.com",
"type": "Email ID"
},
{
"id": "3",
"inputvalue": "Company",
"value": "Google",
"type": "Long Text"
}
]
Simply put, you're trying to reduce an array to a single object, not map one array to another.
var arrayToTraverse = [
{inputvalue:"Name",value:"Tom"},
{inputvalue:"Email",value:"tom#abc.com"},
{inputvalue:"Company",value:"ABC"},
{inputvalue:"Foo",value:"Bar"} // wont show up
];
var valuesRequired = ["Name","Email","Company"];
var result = arrayToTraverse.reduce( (acc, item) => {
if(valuesRequired.includes(item.inputvalue))
acc[item.inputvalue.toLowerCase()] = item.value;
return acc;
}, {});
console.log(result);
Edit: Added lookup array for required fields.
This Object have relationship as: childOne > childTwo > childThree > childFour > childFive > childSix.
{
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"parent_id": "1",
"id": "11"
},
{
"name": "DE",
"parent_id": "2",
"id": "22"
}
],
"childThree": [
{
"name": "ABC",
"parent_id": "22",
"id": "111"
},
{
"name": "DEF",
"parent_id": "11",
"id": "222"
}
],
"childFour": [
{
"name": "ABCD",
"parent_id": "111",
"id": "1111"
},
{
"name": "PQRS",
"parent_id": "111",
"id": "2222"
}
],
"childFive": [
{
"name": "FGRGF",
"parent_id": "1111",
"id": "11111"
},
{
"name": "ASLNJ",
"parent_id": "1111",
"id": "22222"
},
{
"name": "ASKJA",
"parent_id": "1111",
"id": "33333"
}
],
"childSix": [
{
"name": "SDKJBS",
"parent_id": "11111",
"id": "111111"
},
{
"name": "ASKLJB",
"parent_id": "11111",
"id": "222222"
}
]
}
}
Is there any way to delete an item by ID and the objects which are associated with that particular ID should get deleted(i.e., If I do delete parentObj.childTwo[1], then all the related object beneath it should also gets deleted).
Looping manually is too bad code, and generate bugs. There must be better ways of dealing with this kind of problems like recursion, or other.
The data structure does not allow for efficient manipulation:
By nature objects have an non-ordered set of properties, so there is no guarantee that iterating the properties of parentObj will give you the order childOne, childTwo, childThree, ... In practice this order is determined by the order in which these properties were created, but there is no documented guarantee for that. So one might find children before parents and vice versa.
Although the id values within one such child array are supposed to be unique, this object structure does not guarantee that. Moreover, given a certain id value, it is not possible to find the corresponding object in constant time.
Given this structure, it seems best to first add a hash to solve the above mentioned disadvantages. An object for knowing a node's group (by id) and an object to know which is the next level's group name, can help out for that.
The above two tasks can be executed in O(n) time, where n is the number of nodes.
Here is the ES5-compatible code (since you mentioned in comments not to have ES6 support). It provides one example call where node with id "1111" is removed from your example data, and prints the resulting object.
function removeSubTree(data, id) {
var groupOf = {}, groupAfter = {}, group, parents, keep = { false: [], true: [] };
// Provide link to group per node ID
for (group in data) {
data[group].forEach(function (node) {
groupOf[node.id] = group;
});
}
// Create ordered sequence of groups, since object properties are not ordered
for (group in data) {
if (!data[group].length || !data[group][0].parent_id) continue;
groupAfter[groupOf[data[group][0].parent_id]] = group;
}
// Check if given id exists:
group = groupOf[id];
if (!group) return; // Nothing to do
// Maintain list of nodes to keep and not to keep within the group
data[group].forEach(function (node) {
keep[node.id !== id].push(node);
});
while (keep.false.length) { // While there is something to delete
data[group] = keep.true; // Delete the nodes from the group
if (!keep.true.length) delete data[group]; // Delete the group if empty
// Collect the ids of the removed nodes
parents = {};
keep.false.forEach(function (node) {
parents[node.id] = true;
});
group = groupAfter[group]; // Go to next group
if (!group) break; // No more groups
// Determine what to keep/remove in that group
keep = { false: [], true: [] };
data[group].forEach(function (node) {
keep[!parents[node.parent_id]].push(node);
});
}
}
var tree = {"parentObj": {"childOne": [{"name": "A","id": "1"},{"name": "B","id": "2"}],"childTwo": [{"name": "AB","parent_id": "1","id": "11"},{"name": "DE","parent_id": "2","id": "22"}],"childThree": [{"name": "ABC","parent_id": "22","id": "111"},{"name": "DEF","parent_id": "11","id": "222"}],"childFour": [{"name": "ABCD","parent_id": "111","id": "1111"},{"name": "PQRS","parent_id": "111","id": "2222"}],"childFive": [{"name": "FGRGF","parent_id": "1111","id": "11111"},{"name": "ASLNJ","parent_id": "1111","id": "22222"},{"name": "ASKJA","parent_id": "1111","id": "33333"}],"childSix": [{"name": "SDKJBS","parent_id": "11111","id": "111111"},{"name": "ASKLJB","parent_id": "11111","id": "222222"}]}}
removeSubTree(tree.parentObj, "1111");
console.log(tree.parentObj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sure, the function you use to delete an entry should FIRST recurse, which means run itself on the linked entry, unless there is none. So, in psuedocode
function del(name, index)
{
if parent[name][index] has reference
Then del(reference name, reference ID)
Now del parent[name][index]
}
No loop needed.
And since we stop if there is no reference, we do not recurse forever.
Not sure what it is you want but maybe this will work:
const someObject = {
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"childOne": "1",
"id": "11"
},
{
"name": "DE",
"childOne": "2",
"id": "22"
}
]
}
};
const removeByID = (key,id,parent) =>
Object.keys(parent).reduce(
(o,k)=>{
o[k]=parent[k].filter(
item=>
!(Object.keys(item).includes(key)&&item[key]===id)
);
return o;
},
{}
);
const withoutID = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"1" is gone`);
console.log("without key:",JSON.stringify(withoutID,undefined,2));
const otherExample = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","2",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"2" is gone`);
console.log("without key:",JSON.stringify(otherExample,undefined,2));
const both = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",otherExample.parentObj) }
);
console.log(`notice that childTwo items with childOne are both gone`);
console.log("without key:",JSON.stringify(both,undefined,2));
I look ID in an array of objects JSON.
Example JSON:
{
"Przydzial": [{
"M": "Cos",
"Przydzialt": [{
"Name": "",
"Przydz": "tach_1",
"Cos": "Pod",
"Ha": "20",
"ID": "94"
}, {
"Name": "B_K",
"Przydz": "lea",
"Cos": "Chea",
"HA": "8",
"ID": "78"
}
}]
}]
}
Use in controller
var foo = { //my json };
var nowy = $filter('filter')(foo.Przydzialt, { ID:78});
result:
console.log(nowy); // undefined
json is correct - validated in JSLint.
As "$foo.Przydzial" is an array of objects, where every object has its "Przydzialt" attribute, you should execute $filter in a loop:
var newArray;
angular.forEach($foo.Przydzial, function (el) {
newArray = $filter('filter')(el.Przydzialt, {ID: 78});
console.log(newArray);
});