** Update: The answer below works great up to the merge. I had to add some iterations into getL2 and getL3 to make all the calls necessary. The issue seemed to be that some of the tasks from L1 did not have a child at L2, and some L2 did not have an L3. So the final results has some empty arrays. the final result before the merge looks like below. right now, the merge function returns the L1 data with no children added.
var result = [
[{
"ID": 1,
"title": "Task 1",
"org": "A"
}, {
"ID": 2,
"title": "Task 2",
"org": "B"
}],
[{}, {},
{
"ID": 10,
"ParentID": 2
}, {
"ID": 11,
"ParentID": 2
}
],
[{}, {}, {
"ID": 20,
"ParentID": 10
}, {
"ID": 21,
"ParentID": 10
}]
]
I continue to struggle with async and promises, despite the amazing help on stack. I tried to reverse engineer some similar posts with no success. I am trying to chain 3 functions that will make multiple REST calls in SharePoint 2013. the first call grabs all items in list taskL1 that match the organization. it uses the ID's to create a set of unique entries. then a second call against taskL2 is made using the the set to find all entries that have those id's in the parentID column. It then creates a set of those IDs to make a final call against taskL3 and matches those IDs to the parentID column in that list. I need to pass all three results along until a final function will merge the data into a nested object.Bascially, put all the L3 tasks under the correct L2 and all the L2 under the correct L1. I can't seem to keep all 3 results passing along until the end and available to a follow on function. https://jsfiddle.net/75ghvms8/
var setL1 = new Set();
var setL2 = new Set();
var taskL1 = [{"id":1,"title":"Task 1","org":"A"},{"id":2,"title":"Task 2","org":"B"},{"id":3,"title":"Task 3","org":"A"}]
var taskL2 = [{"id":20,"parentID":1},{"id":21,"parentID":1},{"id":22,"parentID":2},{"id":23,"parentID":2}]
var taskL3 = [{"id":100,"parentID":20},{"id":111,"parentID":21},{"id":120,"parentID":22},{"id":220,"parentID":23}]
getL1(taskL1,'A').then(()=>{getL2(taskL2,setL1)}).then(()=>{getL3(taskL3,setL2)});
async function getL1(srcList,org){
const l1List = await getData(srcList,org);
console.log(l1List);
return l1List
}
async function getL2(srcList,set){;
const l2List = await getData2(srcList,set);
console.log(l2List);
return l2List
}
async function getL3(srcList,set){
const l3List = await getData3(srcList,set);
console.log(l3List);
return l3List
}
async function getData(srcList,org,result={}) {
const listItems = await getTaskItems(srcList,org);
result = listItems;
return result;
}
async function getData2(srcList,item,result={}) {
let j = 0;
for(let i of item) {
const listItems = await getTaskItems2(srcList,i)
result[j] = listItems;
j++
}
return result
}
async function getData3(srcList,item,result={}) {
let j = 0;
for(let i of item) {
const listItems = await getTaskItems3(srcList,i)
result[j] = listItems;
j++
}
return result
}
function getTaskItems(srcList,org) {
const arrData = srcList.filter(obj=> {
return obj.org === org;
});
for (let i = 0; i < arrData.length; i++) {
setL1.add(arrData[i].id);
}
return {arrData,setL1}
}
function getTaskItems2(srcList,id) {
const arrData = srcList.filter(obj=> {
return obj.parentID === id;
});
for (let i = 0; i < arrData.length; i++) {
setL2.add(arrData[i].id);
}
return {arrData,setL2}
}
function getTaskItems3(srcList,id) {
const arrData = srcList.filter(obj=> {
return obj.parentID === id;
});
return arrData;
}
// real functions are spRest-Lib functions
/*
function getTaskItems(srcList, org) {
return new Promise((resolve,reject) =>{
sprLib.list(srcList).getItems({
listCols: {
columns here
}
})
.then(function(arrData){
for (let i = 0; i < arrData.length; i++) {
setL1.add(arrData[i].ID);
}
resolve(arrData);
})
.catch(function(errMsg) {console.log(errMsg);});
})
}
*/
You'll need to tweak some key names. Promise chaining is achieved by returning custom objects {l1: [], l2: [], l3: []}. Merge can probably be done as you go along each step, but instead here is one way to do it with explicit merge step.
var taskL1 = [{"id":1,"title":"Task 1","org":"A"},{"id":2,"title":"Task 2","org":"B"},{"id":3,"title":"Task 3","org":"A"}]
var taskL2 = [{"id":20,"parentID":1},{"id":21,"parentID":1},{"id":22,"parentID":2},{"id":23,"parentID":2}]
var taskL3 = [{"id":100,"parentID":20},{"id":111,"parentID":21},{"id":120,"parentID":22},{"id":220,"parentID":23}]
async function getL1(org) {
return new Promise((resolve, reject) => {
// assuming some external reqeust
resolve({ l1:
taskL1.filter((item) => item.org === org)
});
})
}
async function getL2(l1Result) {
return new Promise((resolve, reject) => {
const allL1Ids = Array.from(new Set(Object.values(l1Result.map((item) => item.id))));
// assuming some external request
const filteredList = taskL2.filter((item) => allL1Ids.indexOf(item.parentID !== -1));
resolve({ l1: l1Result, l2: filteredList});
})
}
async function getL3(l1Result, l2Result) {
return new Promise((resolve, reject) => {
const allL2Ids = Array.from(new Set(Object.values(l2Result.map((item) => item.id))));
// assuming some external request
const filteredList = taskL3.filter((item) => allL2Ids.indexOf(item.parentID !== -1));
resolve({l1: l1Result, l2: l2Result, l3: filteredList})
})
}
function groupByKey(items, key) {
return items.reduce(function(results, item) {
(results[item[key]] = results[item[key]] || []).push(item);
return results;
}, {});
}
function merge(l1, l2, l3) {
// we want to put l3.parentID into l2.id.children
let l3ByParentId = groupByKey(l3, "parentID");
let l2ById = groupByKey(l2, "id");
Object.keys(l3ByParentId).forEach((parentId) => {
if (l2ById[parentId]) {
l2ById[parentId][0]['l3children'] = l3ByParentId[parentId];
}
});
let l2ByParentId = groupByKey(Object.values(l2ById).map((item) => item[0]), "parentID");
let l1ById = groupByKey(l1, "id");
Object.keys(l2ByParentId).forEach((parentId) => {
if (l1ById[parentId]) {
l1ById[parentId][0]['l2children'] = l2ByParentId[parentId];
}
});
return Object.values(l1ById).map(item => item[0]);
}
getL1("A")
.then((result) => {
return getL2(result.l1)
})
.then((result) => {
return getL3(result.l1, result.l2)
})
.then((result) => {
return merge(result.l1, result.l2, result.l3)
})
.then((result) => {
console.log(JSON.stringify(result, null, 2));
})
I am trying to extract id from the below array of objects and so far I am able to give it a go with the below code but it is showing undefined and cannot get id , would you please check the code and adjust to get id out?
const array = [{
contact: {
id: 1,
email: 'roar#gmail.com',
},
date: '2/4/22'
},
{
contact: {
id: 2,
email: 'grr#gmail.com',
},
date: '2/4/22'
}
]
function extractValue(arr, prop) {
let extractedValue = [];
for (let i = 0; i < arr.length; ++i) {
// extract value from property
extractedValue.push(arr[i][prop]);
}
return extractedValue;
}
const result = extractValue(array, 'contact.id');
console.log(result);
A good way to do this is the Array Map method
This will get all the id's from your array
const result = array.map((val) => val.contact.id)
const extractValue = (array, path) => {
const [first, second] = path.split('.')
if (second) return array.map(obj => obj[first][second])
return array.map(obj => obj[first])
}
const res = extractValue(array, 'contact.id')
console.log(res)
// => [ 1, 2 ]
this will support single and double level nested results
function find(val, arr) {
for (let x of arr)
val = val[x];
return val;
}
function extractValue(arr, prop) {
return array.map(x => find(x, prop.split(".")))
}
So I have a declared constants variable object. i.e:
const objData = {
project: {
name: '',
age: 0,
subProject: {
name: '',
age: 0
}
}
}
And the function that returns the objData:
const dataSchema = () => {
return objData;
}
The flow is, I want to insert data into my table/collection in my database. So that's why I'm using constants to declare schema, and then I just insert with the returned dataSchema.
This is my func to set value into dataSchema:
const mappingData1 = (data) => {
try {
const dataScheme = Object.assign({}, dataSchema())
dataScheme.project.name = data.name;
dataScheme.project.age = data.age;
return dataScheme;
}catch(err){
return err; //return wrapper error here
}
}
const mappingData2 = (data) => {
try {
const dataScheme = Object.assign({}, dataSchema())
dataScheme.project.subProject.name = data.name;
dataScheme.project.subProject.age = data.age;
return dataScheme;
}catch(err){
return err; //return wrapper error here
}
}
Call mappingData schema func, and insert data into db:
//this bellow statement will be called in insertProjectData func, and called in the first time
const data = { name: 'Someone', age: 7 }
const mappedData = await mappingData1(data)
const result = await this.command.insertData(mappedData)
===================================================================================
//this bellow statement will be called in insertSubProjectData func, and called after insertProjectData func
const data = { name: 'Someone', age: 7 }
const mappedData = await mappingData2(data)
const result = await this.command.insertData(mappedData)
The problem is, when the service is running, and insertSubProjectData func is called after insertProjectData func, why project.name and project.age is setted too with the value from insertProjectData func? even though i didn't set project.name and project.age in insertSubProjectData? It's like the original data (objData) is has been modified. I already use Object.assign too, to create a new object
Any idea please? thank you!
You can guarantee a new object by changing your implementation from this:
const objData = {
project: {
name: '',
age: 0,
subProject: {
name: '',
age: 0
}
}
}
// And the function that returns the objData:
const dataSchema = () => {
return objData;
}
To this:
const dataSchema = () => ({
project: {
name: '',
age: 0,
subProject: {
name: '',
age: 0
}
}
})
Then every time you'll have a real new Object. No more issues
I have a json object with objects inside of it
such as user: {"name": "tim"} and would like a way to turn that in "user.name": 'tim'
I've tried: Javascript Recursion normalize JSON data
Which does not return the result I want, also tried some packages, no luck
You can use a recursive approach to flatten nested objects, by concatenating their keys, as shown below:
const flattenObject = (obj) => {
const flatObject = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
Object.keys(flatNestedObject).forEach((nestedKey) => {
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
});
} else {
flatObject[key] = value;
}
});
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This solution works for any amount of levels.
If your environment does not support Object.keys, you can use for..in instead:
const flattenObject = (obj) => {
const flatObject = {};
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
for (const nestedKey in flatNestedObject) {
if (!flatNestedObject.hasOwnProperty(nestedKey)) continue;
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
}
} else {
flatObject[key] = value;
}
}
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This can easily be done like so:
const myObject = {
user: {
firstname: "john",
lastname: "doe"
}
}
function normalize(suppliedObject = {}) {
const newObject = {};
for (const key of Object.keys(suppliedObject)) {
for (const subKey of Object.keys(suppliedObject[key])) {
newObject[`${key}.${subKey}`] = suppliedObject[key][subKey];
}
}
return newObject;
}
console.log(normalize(myObject));
Be aware that this only normalizes 1 level deep. You can extend the function to normalize all the way down to the last level.
Problem with got data correctly execute function many one times, these function is execute in ngOnInit one time with abstraction but i dont know ocurrs these problem in a server, i thing in snapshotChanges but i don't know.
thx for help
https://i.stack.imgur.com/EinQg.png
return <Observable<Products[]>> t.db.collection(PATHS_FIRESTORE.products).snapshotChanges()
.pipe(
map(actions => {
let arr = actions.map((res) => {
let doc: any = <any>res.payload.doc.data()
let obj: any = {}
if (!isNullOrUndefined(cart)) {
for (const prod in cart) {
if (cart.hasOwnProperty(prod)) {
const element = cart[prod];
if (doc.uid === prod) {
obj[doc.uid] = {
name_product: doc.name_product,
path_img: doc.path_img,
price: doc.price,
quantity: doc.quantity + element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
} else {
t.db.collection(PATHS_FIRESTORE.products).doc(prod).ref.get().then( res => {
const data = res.data()
return obj[res.id] = {
name_product: data.name_product,
path_img: data.path_img,
price: data.price,
quantity: element.total,
uid: doc.uid,
uid_local: doc.uid_local
}
})
}
}
console.log(obj)
}
return obj
}else {
obj = {
...doc
}
return obj
}
})
.filter((b: any) => {
return b.uid_local === uid_local
})
.filter((b: any) => {
return b.quantity > 0
})
.filter((b: any) => {
return !b.status
})
console.log(arr)
return arr
})
)