I have a json file which contains the list of products.
[{"id":76,
"name":"A",
"description":"abc",
"price":199,
"imageUrl":"image.jpg",
"productCategory":[{
"categoryId":5,
"category":null
},{
"categoryId":6,
"category":null
}
]}
I then have a second json file with a list of categories which look like so:
[{"id":5,"name":"red"},
{"id":6,"name”:"blue"}]
What is the best way to join the categories of this two json files in Angular?
This is what I aim to achieve:
[{"id":76,
"name":"A",
"description":"abc",
"price":199,
"imageUrl":"image.jpg",
"productCategory":[{
"categoryId":5,
"category":red
},{
"categoryId":6,
"category":blue
}
]}
You can use filter function for your requirement as below
let products = [{
"id": 76,
"name": "A",
"description": "abc",
"price": 199,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 2,
"category": null
}, {
"categoryId": 1,
"category": null
}]
}, {
"id": 77,
"name": "B",
"description": "abcd",
"price": 1997,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 5,
"category": null
}, {
"categoryId": 6,
"category": null
}]
},
{
"id": 78,
"name": "C",
"description": "abcde",
"price": 1993,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 4,
"category": null
}, {
"categoryId": 6,
"category": null
}]
}];
let category = [{ "id": 5, "name": "red" }, { "id": 6, "name": "blue" }]
let result = products.filter(p => {
var exist = p.productCategory.filter(pc => category.find(c => c.id == pc.categoryId))[0];
return exist;
});
console.log(result);
let products = [{
"id": 76,
"name": "A",
"description": "abc",
"price": 199,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 2,
"category": null
}, {
"categoryId": 1,
"category": null
}]
}, {
"id": 77,
"name": "B",
"description": "abcd",
"price": 1997,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 5,
"category": null
}, {
"categoryId": 6,
"category": null
}]
},
{
"id": 78,
"name": "C",
"description": "abcde",
"price": 1993,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 4,
"category": null
}, {
"categoryId": 6,
"category": null
}]
}];
let category = [{ "id": 5, "name": "red" }, { "id": 6, "name": "blue" }]
let result = products.filter(p => {
var exist = p.productCategory.filter(pc => category.find(c => c.id == pc.categoryId))[0];
return exist;
});
console.log(result);
I make a stackblitz that use a service to retreive the data. Yes, the way is using switchMap and map. SwitchMap receive an array and must return an observable. with map, we transform the data received and return the data transformed
this.dataService.getCategories().pipe(
//first get the categories, the categories is in the
//variable cats
switchMap((cats:any[])=>{
return this.dataService.getProducts().pipe(map((res:any[])=>{
res.forEach(p=>{ //with each product
p.productCategory.forEach(c=>{ //with each productCategory in product
//equals a propertie "category" to the propertie "name" of the cats
c.category=cats.find(x=>x.id==c.categoryId).name
})
})
return res
}))
})).subscribe(res=>{
console.log(res)
})
If only has an unique product we can make
this.dataService.getCategories().pipe(
switchMap((cats:any[])=>{
return this.dataService.getUniqProduct(2).pipe(map((res:any)=>{
res.productCategory.forEach(c=>{
c.category=cats.find(x=>x.id==c.categoryId).name
})
return res
}))
})).subscribe(res=>{
console.log(res)
})
We can improve our dataService "cached" the categories
getCategories() {
if (this.categories)
return of(this.categories);
return http.get(......).pipe(tap(res=>{
this.categories=res;
}))
}
NOTE:In the stackbit I simulate the call to an http.get(...) using "of"
Related
I have an 2 array, first array:
[
{
"id": 1,
"name": "jumlah_sd",
"title": "Jumlah SD",
"value": "1222"
},
{
"id": 2,
"name": "jumlah_smp",
"title": "Jumlah SMP",
"value": "1322"
}
]
and second array with no value:
[
{
"id": 1,
"name": "jumlah_sd",
"title": "Jumlah SD"
},
{
"id": 2,
"name": "jumlah_smp",
"title": "Jumlah SMP"
},
{
"id": 3,
"name": "jumlah_sma",
"title": "Jumlah SMA"
}
]
My question is, how to push by their "id" object value in first array to second array in JavaScript? And if there is no value in other object, so the value become "value": "" in second array.
One way to do is to create an object with the id as the key and value as value for easy lookup.
After that map through the second array and add the id using the lookup object.
const arr1 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD", "value": "1222" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP", "value": "1322" }]
let arr2 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP" },{ "id": 3, "name": "jumlah_sma", "title": "Jumlah SMA" }]
const idLookup = arr1.reduce((acc,{id,value}) => {
acc[id]??=value
return acc
},{})
arr2 = arr2.map((x) => ({...x, value: idLookup[x.id]||""}))
console.log(arr2)
Another approach is to use find inside map but it will intuitively be slower since the find is called in each iteration inside the map
const arr1 = [ { "id": 1, "name": "jumlah_sd","title": "Jumlah SD", "value": "1222" }, { "id": 2,"name": "jumlah_smp", "title": "Jumlah SMP", "value": "1322" }]
let arr2 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP" }, { "id": 3, "name": "jumlah_sma", "title": "Jumlah SMA" }]
arr2 = arr2.map((x) => {
const entry = arr1.find(({id}) => x.id === id)
return {...x,value:entry?.value||''}
})
console.log(arr2)
I have nested array like following.
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
I need to traverse through tree.children array to get all id and name of sub arrays and its children until we didn't find children array empty. (Note: children array may be empty or may have further levels)
I need result like follows
Expected result
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "test east sub 1",
"value": 11,
"selected": false
},
{
"name": "sub 22 sub 1",
"value": 12,
"selected": false
},
{
"name": "sub 333 of sub",
"value": 15,
"selected": false
},
{
"name": "sub 4444",
"value": 16,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
},
{
"name": "test west sub 1",
"value": 14,
"selected": false
},
]
I tried following
const candidates = tree.children.map(org => ({name: org.name, value: org.id, selected: false}));
but it gives me following
[
{
"name": "East Region",
"value": 10,
"selected": false
},
{
"name": "west region",
"value": 13,
"selected": false
}
]
I am trying to get that but not sure how I can put condition that traverse until children is empty and push required fields in final array in required format. May need recursive/call back functions but not sure how I can use that.
Please help to get expected result. Thanks
Try this,
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [{
"id": 11,
"name": "test east sub 1",
"children": [{
"id": 12,
"name": "sub 22 sub 1",
"children": [{
"id": 15,
"name": "sub 333 of sub ",
"children": [{
"id": 16,
"name": "sub 4444",
"children": []
}]
}]
}]
}]
},
{
"id": 13,
"name": "west region",
"children": [{
"id": 14,
"name": "test west sub 1",
"children": []
}]
}
]
}
let items = []
let result = lootIt(tree.children)
console.log(result)
function lootIt (arr) {
for(let i = 0 ; i< arr.length ; i++){
let obj = {}
obj['name'] = arr[i]['name']
obj['value'] = arr[i]['id']
obj['selected'] = false
items.push(obj)
if(arr[i].children !== 0){
lootIt(arr[i].children)
}
}
return items
}
You can use recursion to do it
const tree = {
"id": 1,
"name": "mainOrgName",
"children": [
{
"id": 10,
"name": "East Region",
"children": [
{
"id": 11,
"name": "test east sub 1",
"children": [
{
"id": 12,
"name": "sub 22 sub 1",
"children": [
{
"id": 15,
"name": "sub 333 of sub ",
"children": [
{
"id": 16,
"name": "sub 4444",
"children": []
}
]
}
]
}
]
}
]
},
{
"id": 13,
"name": "west region",
"children": [
{
"id": 14,
"name": "test west sub 1",
"children": []
}
]
}
]
}
function mapTree(children){
let result =[]
for(c of children){
result.push({name: c.name, value: c.id, selected: false})
result = result.concat(mapTree(c.children))
}
return result
}
console.log(mapTree(tree.children))
I have below JSON for example where I need to extract all categories Id from the last subcategory if its not empty. For instance: On below JSON I need Ids like mens-apparel-ricky inside last categories object but if this categories was empty I like to extract the last ID like womens-new-arrivals. Basically it should be the list of all these ID's extracted.
Thanks in Advance
"categories": [{
"id": "featured",
"name": "NEW ARRIVALS",
"categories": [{
"id": "featured-new-arrivals",
"name": "New Arrivals",
"categories": [{
"id": "mens-new-arrivals",
"name": "Mens",
"categories": []
}, {
"id": "womens-new-arrivals",
"name": "Womens",
"categories": [{
"id": "mens-apparel-ricky",
"name": "Relaxed",
"categories": []
}, {
"id": "new-arrivals-kids",
"name": "Kids",
"categories": []
}, {
"id": "new-arrivals-accessories",
"name": "Accessories",
"categories": []
}]
}, {
"id": "collections",
"name": "Featured",
"categories": [{
"id": "spring-shop",
"name": "Spring Shop",
"categories": [{
"id": "spring-shop-mens",
"name": "Spring Shop Men's",
"categories": []
}, {
"id": "spring-shop-womens",
"name": "Spring Shop Women's",
"categories": []
}]
}, {
"id": "the-festival-shop",
"name": "The Festival Shop",
"categories": [{
"id": "mens-festival-shop",
"name": "Mens Festival Shop",
"categories": []
}, {
"id": "womens-festival-shop",
"name": "Womens Festival Shop",
"categories": []
}]
}, {
"id": "buddha-collections",
"name": "The Icons Shop",
"categories": [{
"id": "buddha-collections-men",
"name": "Mens",
"categories": []
}, {
"id": "buddha-collections-women",
"name": "Womens",
"categories": []
}]
}, {
"id": "all-black",
"name": "All Black Everything",
"categories": []
}]
}]
}, {
"id": "denim",
"name": "DENIM",
"categories": [{
"id": "denim-view-all",
"name": "Shop All Denim",
"categories": [{
"id": "denim-view-all-mens",
"name": "Mens Denim",
"categories": []
}, {
"id": "denim-view-all-womens",
"name": "Womens Denim",
"categories": []
}, {
"id": "denim-view-all-kids",
"name": "Kids Denim",
"categories": []
}]
}, {
"id": "denim-collections",
"name": "Featured",
"categories": [{
"id": "denim-collections-signature-stitch",
"name": "Big T & Super T Stitch",
"categories": []
}, {
"id": "denim-collections-lighten-up",
"name": "Light Wash Denim",
"categories": []
}, {
"id": "dark-wash-denim",
"name": "Dark Wash Denim",
"categories": []
}]
}]
}]
}
This is what I have tried but being new to coding getting difficult to get list form this complex structure.
var rootCategories = (jsonData.categories);
for (var i=0; i < rootCategories.length; i++)
{
var firstChild = (jsonData.categories[i].categories);
for (var j=0; j < firstChild.length; j++)
{
if ((firstChild[j].categories[j].length != 0)) {
catId = firstChild[j].categories[j].id
console.log(catId);
}
}
}
Below is one possible way to achieve the target.
Code Snippet
// recursive method to get "id"s
const getIds = arr => (
arr.flatMap( // iterate using ".flatMap()" to avoid nesting
({id, categories}) => { // de-structure to directly access "id" & "categories"
if ( // if "categories" is not empty, recurse to next level
Array.isArray(categories) &&
categories.length > 0
) {
return getIds(categories);
} else { // if it is empty, simply return the id
return id;
}
}
)
);
const rawData = {
"categories": [{
"id": "featured",
"name": "NEW ARRIVALS",
"categories": [{
"id": "featured-new-arrivals",
"name": "New Arrivals",
"categories": [{
"id": "mens-new-arrivals",
"name": "Mens",
"categories": []
}, {
"id": "womens-new-arrivals",
"name": "Womens",
"categories": [{
"id": "mens-apparel-ricky",
"name": "Relaxed",
"categories": []
}, {
"id": "new-arrivals-kids",
"name": "Kids",
"categories": []
}, {
"id": "new-arrivals-accessories",
"name": "Accessories",
"categories": []
}]
}, {
"id": "collections",
"name": "Featured",
"categories": [{
"id": "spring-shop",
"name": "Spring Shop",
"categories": [{
"id": "spring-shop-mens",
"name": "Spring Shop Men's",
"categories": []
}, {
"id": "spring-shop-womens",
"name": "Spring Shop Women's",
"categories": []
}]
}, {
"id": "the-festival-shop",
"name": "The Festival Shop",
"categories": [{
"id": "mens-festival-shop",
"name": "Mens Festival Shop",
"categories": []
}, {
"id": "womens-festival-shop",
"name": "Womens Festival Shop",
"categories": []
}]
}, {
"id": "buddha-collections",
"name": "The Icons Shop",
"categories": [{
"id": "buddha-collections-men",
"name": "Mens",
"categories": []
}, {
"id": "buddha-collections-women",
"name": "Womens",
"categories": []
}]
}, {
"id": "all-black",
"name": "All Black Everything",
"categories": []
}]
}]
}, {
"id": "denim",
"name": "DENIM",
"categories": [{
"id": "denim-view-all",
"name": "Shop All Denim",
"categories": [{
"id": "denim-view-all-mens",
"name": "Mens Denim",
"categories": []
}, {
"id": "denim-view-all-womens",
"name": "Womens Denim",
"categories": []
}, {
"id": "denim-view-all-kids",
"name": "Kids Denim",
"categories": []
}]
}, {
"id": "denim-collections",
"name": "Featured",
"categories": [{
"id": "denim-collections-signature-stitch",
"name": "Big T & Super T Stitch",
"categories": []
}, {
"id": "denim-collections-lighten-up",
"name": "Light Wash Denim",
"categories": []
}, {
"id": "dark-wash-denim",
"name": "Dark Wash Denim",
"categories": []
}]
}]
}]
}]
};
console.log(getIds(rawData.categories));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added in the snippet above.
If you're not afraid of reference loops (shouldn't technically happen in a serialized REST response) you can try recurring function as the simplest way to achieve this
function getIDs(node, isRoot = false) {
if (!node.categories.length) // if I understood your question correctly, you don't want parents to be printed out, otherwise just check for isRoot instead
return isRoot || console.log(node.id);
// with above you won't access non-existent id of the root collection
// because it will stop checking after first true statement
for (const category of categories)
getIDs(category);
}
getIDs(collection, true);
I have an array with objects
const data = [{
"id": 19887003,
"category": "Shops",
"details": "Shoe Store",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 19234003,
"category": "Shops",
"details": "Shoe Point",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 190456003,
"category": "Food",
"details": "Korean",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 190111003,
"category": "Food",
"details": "Chinese",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 1902303,
"category": "Food",
"details": "Lounge",
"star": 4,
"subCategory": "Bar",
}]
this is a small piece but the structure is the same for all objects: i have a category, with multiple subcategories and sometimes the subcategory has details..for example the category food has the subcategory restourant and restourant has many types (chinese, korean).
My goal is to get a structure like that:
[
{
"category": "Food",
"subCategories": [
{
"Subcategory": "Bar",
"details": [
{
name: "Lounge",
star: 2,
id: 1902303
}
]
},
{
"Subcategory": "Restaurant",
"details": [
{
name: "Chinese",
star: 4,
id: 190111003
},
{
name: "Korean",
star: 4,
id: 190456003
}
]
}
},
{
"category": "Shops",
"subCategories": [
{
"Subcategory": "Outlet",
"details": [
{
name: "Shoe Store",
star: 2,
id: 19887003
},
{
name: "Shoe Point",
star: 2,
id: 19234003
}
]
}
]
}
]
My attempt:
const groupedCategories = data.reduce((accumulator, element) => {
const detail = element.details;
const category = element.category;
const subCategory = element.subCategory;
if (accumulator[category]){
return {
...accumulator,
[category]: {
...accumulator[category],
subCategories: [...new Set([...accumulator[category].subCategories,subCategory])],
}
}}
else {
return {
...accumulator,
[category]: {
subCategories: [subCategory],
}
}
}
}, {});
I tried use reduce method like that but this is not the exact structure I desire in particular how to put details fields into subcategories.
Thanks
array.reduce seems to be the right choice. Simplest approach is to have double if statement to check if previous element (category and subcategory exists) and either push into existing array or create new object on upper level:
const data = [{
"id": 19887003,
"category": "Shops",
"details": "Shoe Store",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 19234003,
"category": "Shops",
"details": "Shoe Point",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 190456003,
"category": "Food",
"details": "Korean",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 190111003,
"category": "Food",
"details": "Chinese",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 1902303,
"category": "Food",
"details": "Lounge",
"star": 4,
"subCategory": "Bar",
}]
let output = data.reduce((acc,cur) => {
let {category, subCategory, ...rest} = cur;
let prevCat = acc.find(x => x.category === category);
if(!prevCat){
acc.push({category, subCategories: [{subCategory, details: [rest]}]});
} else {
let prevSubCat = prevCat.subCategories.find(x => x.subCategory === subCategory);
if(!prevSubCat) {
prevCat.subCategories.push({subCategory, details: [rest]});
} else {
prevSubCat.details.push(rest);
}
}
return acc;
}, []);
console.log(output);
I'm trying to loop thru products array and find its description in description array. The product id and description parent represent the same product. If the description could be found, push the product with its description to the results array.
I don't really know how this loop should look like.
Products
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
Descriptions
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
Expected output results
let results = [
{
"id": "76",
"titel": "Battery",
"price": "34"
"description": "Good battery",
"parent": "76"
},{
"id": "102",
"titel": "Pen",
"price": "45"
"description": "Sharp pen",
"parent": "102"
},
]
You can take advantage of Array.prototype.reduce which allows to transform an array to another type of Object of array.
combine with the Array.prototype.find to check if item with a given id exists in the descriptions Array
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
const result = products.reduce((acc, item) => {
// Check if the description of the current product exists
let exist = descriptions.find(desc => {
return item.id === desc.parent;
});
if(exist) {
return acc.concat({...item, description: exist.description});
}
return acc;
}, []);
console.log(result);
If you add another array comments you can update the code instead of to concatenate the item in the accumulator directly you'll create another object which after finding the related comment in the comment array you'll add the comment key in that object with their comment.
Here is the code
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
let comments = [
{
"comment": "Good battery comment",
"product": "76",
}, {
"comment": "Sharp pen comment",
"product": "102"
}
];
const result = products.reduce((acc, item) => {
// Check if the description of the current product exists
let productExists = descriptions.find(desc => {
return item.id === desc.parent;
});
let commentExists = comments.find(comment => {
return item.id === comment.product
});
let newItem = null;
if(productExists) {
newItem = {
...item,
description: productExists.description
};
}
if(commentExists) {
newItem.comment = commentExists.comment;
}
return newItem? acc.concat(newItem): acc;
}, []);
console.log(result);
You should iterate over the descriptions, then use there Array.find and merge them together into a new object, with Object.assign and push them to your results
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
];
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
];
let results = [];
for (const desc of descriptions) {
const product = products.find(v => v.id === desc.parent);
if (!product) {
continue;
}
results.push(Object.assign({}, product, desc));
}
console.log(results);
const result = descriptions.map(descr => {
const product = products.find(prod => prod.id === descr.parent);
return {...product, ...descr}
})