I have a structure in which the number of arrangements can vary:
array1 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 4, name: 'local4'}},
{local: {id: 5, name: 'local5'}}
];
array2 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local4'}},
{local: {id: 3, name: 'local5'}},
];
array3 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local5'}},
];
I need to create a new array from these, in which this new array is ordered first by the ids that are repeated in all the arrays and then the ones that are not repeated, should be something like this:
newArray = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 5, name: 'local5'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 4, name: 'local4'}}
]
Someone who can help me please!!
Converting all the arrays to objects for fast searching.
const array1 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 2,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 4,
name: 'local4'
}
},
{
local: {
id: 5,
name: 'local5'
}
}
];
const array2 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local4'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const array3 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const obj1 = array1.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj2 = array2.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj3 = array3.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const result = {
...obj3,
...obj2,
...obj1
};
const output = [];
const temp = [];
for (let key in result) {
if (obj1[key] && obj2[key] && obj3[key]) {
output.push(result[key]);
} else temp.push(result[key]);
}
console.log([...output, ...temp]);
I would do it like this (may not be the optimum solution):
/* Same Arrays as yours */ const array1=[{local:{id:1,name:"local1"}},{local:{id:2,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:4,name:"local4"}},{local:{id:5,name:"local5"}}],array2=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local4"}},{local:{id:3,name:"local5"}}],array3=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local5"}}];
function myFunc(arrays) {
// All items, with duplicates
const allItems = [].concat.apply([], arrays);
// All IDs, without duplicates thanks to `Set`
const allIDs = Array.from(
allItems.reduce((set, item) => set.add(item.local.id), new Set())
);
// Helper function used for sorting
const isInAllArrays = id => arrays.every(
arr => arr.some(item => item.local.id === id)
);
// Sort the IDs based on whether they are in all arrays or not
allIDs.sort((a, b) => {
const _a = isInAllArrays(a), _b = isInAllArrays(b);
if (_a !== _b) return _a ? -1 : 1;
return 0;
});
// Map all IDs to the first element with this ID
return allIDs.map(id => allItems.find(item => item.local.id === id));
}
const newArray = myFunc([array1, array2, array3]);
// Just for readability in the demo below
console.log(JSON.stringify(newArray).split('},{').join('},\n{'));
1) Traverse all arrays and build an object with keys as id and value include object and also maintain the frequency of occurrence (count).
2) Now, Object.values of above object and sort them based on 'count'.
You will get most frequent items at top.
const sort = (...arrs) => {
const all = {};
arrs
.flat()
.forEach(
(obj) =>
(all[obj.local.id] =
obj.local.id in all
? { ...all[obj.local.id], count: all[obj.local.id].count + 1 }
: { ...obj, count: 1 })
);
return Object.values(all)
.sort((a, b) => b.count - a.count)
.map(({ count, ...rest }) => rest);
};
array1 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 2, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 4, name: "local4" } },
{ local: { id: 5, name: "local5" } },
];
array2 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local4" } },
{ local: { id: 3, name: "local5" } },
];
array3 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local5" } },
];
console.log(sort(array1, array2, array3))
Related
I am having a problem trying to modify the name of a nested object using map function and to return the modified object.
I was trying the approach with double forEach loop but I am also failing with that.
const myObject = [{
id: 1,
childrenList: [{
id: 1,
name: 'foo',
},
{
id: 2,
name: 'foo',
},
],
},
{
id: 2,
childrenList: [{
id: 1,
name: 'foo',
},
{
id: 2,
name: 'foo',
},
],
},
];
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
thisChild.childrenList.map((item) => {
if (item.id === 1) {
return {
...item,
name: 'bar',
};
}
return item;
});
}
return thisChild;
});
console.log(alteredObject);
//trying to get:
alteredObject = [
{
id: 1,
childrenList: [
{
id: 1,
name: 'bar',
},
{
id: 2,
name: 'foo',
},
],
},
{
id: 2,
childrenList: [
{
id: 1,
name: 'foo',
},
{
id: 2,
name: 'foo',
},
],
},
];
This is the first time I am trying to modify a nested object. Normally with an array of objects, I am not having any issue so I am not sure what I am doing wrong
You only need to update the children with your map and it will work. Like this:
const myObject = [
{
id: 1,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
},
{
id: 2,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
}
];
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
thisChild.childrenList = thisChild.childrenList.map((item) => {
if (item.id === 1) {
return {
...item,
name: "bar"
};
}
return item;
});
}
return thisChild;
});
console.log(alteredObject);
And if you want to do it with forEach:
const myObject = [
{
id: 1,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
},
{
id: 2,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
}
];
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
thisChild.childrenList.forEach((item) => {
if (item.id === 1) {
item.name = 'bar';
}
return item;
});
}
return thisChild;
});
console.log(alteredObject);
If you can modify your object then you can do it with two forEach:
const myObject = [
{
id: 1,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
},
{
id: 2,
childrenList: [
{
id: 1,
name: "foo"
},
{
id: 2,
name: "foo"
}
]
}
];
myObject.forEach((thisChild) => {
if (thisChild.id === 1) {
thisChild.childrenList.forEach((item) => {
if (item.id === 1) {
item.name = 'bar';
}
return item;
});
}
});
console.log(myObject);
As you already know, Array.prototype.map() returns a new Array containing the modified version.
In your first map function myObject.map(), you aren't saving the second map function modified result as the childrenList content.
therefore no changes would be stored in the first map function and the result would have no changes.
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
// Here you should save the result of this
// Array.prototype.map() Function as the new 'thisChild.childrenList'
thisChild.childrenList = thisChild.childrenList.map((item) => {
// ...
});
}
return thisChild;
});
const myObject = [{
id: 1,
childrenList: [{
id: 1,
name: 'foo',
},
{
id: 2,
name: 'foo',
},
],
},
{
id: 2,
childrenList: [{
id: 1,
name: 'foo',
},
{
id: 2,
name: 'foo',
},
],
},
];
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
thisChild.childrenList = thisChild.childrenList.map((item) => {
if (item.id === 1) {
return {
...item,
name: 'bar',
};
}
return item;
});
}
return thisChild;
});
console.log(alteredObject);
You can use this code :
const myObject = [
{
id: 1,
childrenList: [
{ id: 1, name: 'foo', },
{ id: 2, name: 'foo', },
],
},
{
id: 2,
childrenList: [
{ id: 1, name: 'foo', },
{ id: 2, name: 'foo', },
],
},
];
let result = myObject.map(
el => el.id === 1 ?
{...el, childrenList: el.childrenList.map(child => child.id === 1 ? {...child, name: 'bar'} : child)}
: el
);
console.log(result);
This can be done with a couple of map calls, we'll alter the name value if the firstChild id is 1 and the leaf object id is also 1:
const myObject = [ { id: 1, childrenList: [ { id: 1, name: 'foo', }, { id: 2, name: 'foo', }, ], }, { id: 2, childrenList: [ { id: 1, name: 'foo', }, { id: 2, name: 'foo', }, ], }, ];
const alteredObject = myObject.map((thisChild) => {
return { ...thisChild, childrenList: thisChild.childrenList.map(({id, name}) => {
return { id, name: (thisChild.id === 1 && id === 1) ? 'bar': name };
})}
});
console.log(alteredObject)
.as-console-wrapper { max-height: 100% !important; }
The array map method creates a new array (mdn), so the parent object alteredObject still has the childrenList key pointing to the original array.
To solve this, you can add assignment of the new array to the key:
thisChild.childrenList = thisChild.childrenList.map(...)
This way, the key will point to the newly created array
You're missing a return; you have to return the modified thisChild as {...thisChild, childrenList:modifiedChildrenList}
const myObject = [{ id: 1, childrenList: [{ id: 1, name: 'foo', }, { id: 2, name: 'foo', }, ], }, { id: 2, childrenList: [{ id: 1, name: 'foo', }, { id: 2, name: 'foo', }, ], }, ];
const alteredObject = myObject.map((thisChild) => {
if (thisChild.id === 1) {
return {...thisChild,childrenList:thisChild.childrenList.map((item) => {
if (item.id === 1) {
return {
...item,
name: 'bar',
};
}
return item;
})
}
}
return thisChild;
});
console.log(alteredObject);
I have an object with a structure like below
const data = [
{ academicYearId: 1, classLevelId: 1, subjectId: 1, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 2, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 3, ...},
,,,
]
I need to create a function that will return unique columns e.g
const uniqueColumns = ( val, columns)=> {
//
}
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
let result = uniqueColumns(val)
console.log(val)
/**
* Expected
* [{ id: 1, name: 'n1'}, { id: 2, name: 'n2'}, { id: 3, name: 'n2'}]
*/
}
I have tried to look at the various answers in the post How to get distinct values from an array of objects in JavaScript? and I have managed to come up with the below
const uniqueColumns = (val, columns) =>
([...new Set(
val.map(item =>
columns.reduce((prev, next) =>
({[next]: item[next], ...prev}), {})
).map(item => JSON.stringify(item)))
].map(item => JSON.parse(item)))
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
const result = uniqueColumns(val, ['id', 'name'])
console.log(result)
What I was inquiring is if there is a better approach instead of having to Convert Object to string and back to object to achieve this
You can use array reduce method.
const val = [
{ id: 1, name: "n1", val: 1 },
{ id: 1, name: "n1", val: 2 },
{ id: 2, name: "n2", val: 1 },
{ id: 3, name: "n2", val: 2 },
];
const uniqueColumns = (val, columns) => {
let ret = val.reduce((p, c) => {
let obj = {};
columns.forEach((x) => (obj[x] = c[x]));
let key = Object.values(obj);
if (!p[key]) p[key] = obj;
return p;
}, {});
return Object.values(ret);
};
const result = uniqueColumns(val, ["id", "name"]);
console.log(result);
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
i neeed to merge two arrays: Categories and Products. Each product has a category object. I need to organize by category, include the category object and keep the empty categories. GroupBy function include only one parameter.
const Categories= [
{id: 1, 'name': 'category1'}
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'},
]
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
]
expected result
const result = [
{
category: {id: 1, name: 'category1'},
products:[{id:1, name: 'produt1'}, {id: 2, name: 'produto1'} ]
},
{
category: {id: 2, name: 'category2'},
products:[{id:3, name: 'produt3'}, {id: 4, name: 'produto4'} ]
},
{
category: {id: 3, name: 'category3'},
products:[]
},
{
category: {id: 4, name: 'category4'},
products:[]
},
]
attempts:
for (i = 0; i < categoriesJson.length; i++) {
categoriesJson[i] = _.assign({}, categoriesJson[i], { products: [] })
for (j = 0; j < productsJson.length; j++) {
if(productsJson[j].categoryId.objectId === categoriesJson[i].objectId){
categoriesJson[i].products.push(productsJson[j])
}
}
}
Concat the Categories (formatted by to a Product format) to the Products, group by the category.id, and then map each group - category is taken from the 1st item, while products are the the items in groups, without the category, and empty items are rejected:
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = _(Products)
.concat(Categories.map(category => ({ category })))
.groupBy('category.id')
.map(group => ({
category: _.head(group).category,
products: _(group)
.map(o => _.omit(o, 'category'))
.reject(_.isEmpty)
.value()
}))
.value()
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
And the same idea with lodash/fp. Wrap the _.flow() with the _.useWith() function, and preformat the Categories (2nd param) to fit the Categories. The rest is similar to the lodash chain.
const { useWith, identity, flow, concat, groupBy, map, head, omit, reject, isEmpty } = _
const formatProducts = flow(map(omit('category')), reject(isEmpty))
const fn = useWith(flow(
concat,
groupBy('category.id'),
map(group => ({
category: head(group).category,
products: formatProducts(group)
}))
), [identity, map(category => ({ category }))])
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = fn(Products, Categories)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
If lodash is not a requirement in the solution, this is how I did it with plain javascript;
const Categories= [
{id: 1, 'name': 'category1'},
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'}
];
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
];
const result = [];
for (let index in Categories) {
let category_id = Categories[index].id;
result.push({
category: Categories[index],
products: GetProductsWithCategoryId(category_id)
});
}
function GetProductsWithCategoryId(category_id) {
let products = [];
for (let index in Products) {
if (Products[index].category.id == category_id) {
products.push({
id: Products[index].id,
name: Products[index].name
});
}
}
return products;
}
console.log("result:", result);
Using reduce, create a mappedProducts object which groups the Products based on the category.id. Like this:
{
"1": [{ id: 1, name: "product1" }, { id: 2, name: "product2" }],
"2": [{ id: 3, name: "product3" }, { id: 4, name: "product4" }]
}
Then, map the Categories array and get the output for each category
const Categories=[{id:1,name:"category1"},{id:2,name:"category2"},{id:3,name:"category3"},{id:4,name:"category4"},],
Products=[{id:1,name:"product1",category:{id:1,name:"category1"}},{id:2,name:"product2",category:{id:1,name:"category1"}},{id:3,name:"product3",category:{id:2,name:"category2"}},{id:4,name:"product4",category:{id:2,name:"category2"}}];
const mappedProducts = Products.reduce((acc, { category, ...rest }) => {
acc[category.id] = acc[category.id] || [];
acc[category.id].push(rest)
return acc;
}, {})
const output = Categories.map(category => ({
category,
products: mappedProducts[category.id] || []
}))
console.log(output)
In a single function. Lodash is not necessary:
const Categories = [
{ id: 1, name: "category1" },
{ id: 2, name: "category2" },
{ id: 3, name: "category3" },
{ id: 4, name: "category4" }
];
const Products = [
{ id: 1, name: "product1", category: { id: 1, name: "category1" } },
{ id: 2, name: "product2", category: { id: 1, name: "category1" } },
{ id: 3, name: "product3", category: { id: 2, name: "category2" } },
{ id: 4, name: "product4", category: { id: 2, name: "category2" } }
];
function combine(categories, products) {
return categories.reduce((list, category) => {
const nextItem = {
category,
products: [
products.filter(p => p.category.id === category.id).map(
({ id, name }) => ({
id,
name
})
)
]
};
list.push(nextItem);
return list;
}, []);
}
const result = combine(Categories, Products)
Now for your information, if you had a huge list of categories and/or products, this wouldn't be the ideal solution as there is a lot of looping involved. Instead, you would cache products in such a way that you only ever need to look at a given product once (rather than looking at every product for every category). With a small data set, this optimization isn't necessary.
I have a Object which looks like the following obj.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
];
The Object should be converted into the following scheme:
var items = [
{ id: 11, name: "fruits", items: [
{ id: 32, name: "banana" }
]},
{ id: 10, name: "threes" },
{ id: 1, name: "animals", items: [
{ id: 2, name: "cat" },
{ id: 3, name: "dog" },
{ id: 4, name: "weazle", items: [
{ id: 5, name: "sand shadow weazle" }
]}
]},
{ id: 15, name: "cars" }
];
I tried a lot but unfortunately without any success. I did $.each on obj, did a split('_') on it and pushed it to items. But how can I do it for unlimited depth and push it into the right category?
I'm happy for any help.
Maybe this helps.
It works with Array.prototype.forEach for processing obj, Array.prototype.reduce for getting the right branch and Array.prototype.some for the right array element for inserting the new object.
This proposal works for sorted and consistent data.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
],
tree = [];
obj.forEach(function (a) {
var path = a.name.split('_'),
o = {};
o.id = a.id;
path.reduce(function (r, b) {
o.name = b;
r.some(function (c) {
if (c.name === b) {
c.items = c.items || [];
r = c.items;
return true;
}
});
return r;
}, tree).push(o);
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');
Update: Version for independent order of items.
var obj = [
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 32, name: "fruits_banana" },
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 11, name: "fruits" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" },
{ id: 999, name: "music_pop_disco_euro"}
],
tree = [];
obj.forEach(function (item) {
var path = item.name.split('_'),
o = tree;
path.forEach(function (a, i) {
var oo = { name: a, items: [] },
last = path.length - 1 === i,
found = o.some(function (b) {
if (b.name === a) {
if (last) {
b.id = item.id;
return true;
}
b.items = b.items || [];
o = b.items;
return true;
}
});
if (!found) {
if (last) {
o.push({ id: item.id, name: a });
} else {
o.push(oo);
o = oo.items;
}
}
});
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');