Build one object by using array of objects (recursively with subobjects) - javascript

Maybe someone can help here.
What I want to do is:
Build one result obj that consists of all the attributes / subobjects of all of these objects in the array
Always have only arrays in the result obj
Recursively build this object (as I don't know how many levels there could be and I don't know the names of the objects)
The order of the attributes is not relevant
Managable Except / black list for some attributes (such as id)
const arr = [{
id: 0,
nickname: 'Testnick 0',
loel: {
nice: 'like it',
},
rating: {
abc: 5,
helloworld: 2,
},
},
{
id: 1,
nickname: 'Testnick 2',
rating: {
abc: 4,
moep: 1,
},
},
{
id: 2,
nickname: 'Testnick 3',
rating: {
abc: 40,
sun: 20,
anotherObj: {
tr: 34,
subsubobj: {
sd: 24,
},
},
},
},
];
So the resultobj would look similar to this:
const result = {
id: [0, 1, 2],
nickname: ['Testnick 0', 'Testnick 2', 'Testnick 3'],
loel: {
nice: ['like it'],
},
rating: {
abc: [5, 4, 40],
helloworld: [2],
moep: [1],
sun: [20],
anotherObj: {
tr: [34],
subsubobj: {
sd: [24],
},
},
},
};
Can someone help here?

You coud reduce the given array and iterate the object's keys and values recursively for nested objects.
function setValues(target, source) {
Object.entries(source).forEach(([k, v]) => {
if (v && typeof v === 'object') {
setValues(target[k] = target[k] || {}, v);
} else {
target[k] = target[k] || [];
target[k].push(v);
}
});
return target;
}
var data = [{ id: 0, nickname: 'Testnick 0', loel: { nice: 'like it' }, rating: { abc: 5, helloworld: 2 } }, { id: 1, nickname: 'Testnick 2', rating: { abc: 4, moep: 1 } }, { id: 2, nickname: 'Testnick 3', rating: { abc: 40, sun: 20, anotherObj: { tr: 34, subsubobj: { sd: 24 } } } }],
result = data.reduce(setValues, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Getting an array from array of array objects

I have an array which consists of an array objects as shown:
dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
I need to extract an array consisting of only arrObj objects:
var newArr = [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
},
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
];
Tried using reduce method unsuccessfully:
dataArr.reduce((previousValue, currentValue, currentIndex, array) => {
return previousValue. arrObj.concat(currentValue.arrObj)
});
Let me know how to do this. Thanks
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let result = dataArr.flatMap(e => e.arrObj)
console.log(result)
You were pretty close.
There's no arrObj property in your result, it's just an array.
You need to provide an empty array as the initial value argument to reduce().
const dataArr = [{
id: 1,
arrObj: [{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
const newArr = dataArr.reduce((previousValue, currentValue) => {
return previousValue.concat(currentValue.arrObj)
}, []);
console.log(newArr);
You could use in one line by using Spread Operator...
dataArr.reduce((previousValue, currentValue) => [...previousValue?.arrObj, ...currentValue?.arrObj]);
Tip: use Optional chaining ?. in case there is no property arrObj!
You can use the Array#reduce method as follows:
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let newArr = dataArr.reduce((acc,cur) => [...acc, ...cur.arrObj], []);
console.log( newArr );

How to sort by a specific value with localeCompare?

I want to sort my current array by having live at the front, instead of sorting it alphabetically so that the items with live will be at the front, followed by schedule and lastly end, but the way I'm doing it now with localeCompare, it will only sort alphabetically.
So how can I do so to sort the array in a specific way that I want?
Array Before Sorting Example
const [bulletins] = useState([
{
id: 1,
liveStatus: 'live',
},
{
id: 2,
liveStatus: 'live',
},
{
id: 3,
liveStatus: 'end',
},
{
id: 4,
liveStatus: 'schedule',
},
{
id: 5,
liveStatus: 'end',
}
]);
Sorted Array Example
const [bulletins] = useState([
{
id: 3,
liveStatus: 'end',
},
{
id: 5,
liveStatus: 'end',
},
{
id: 2,
liveStatus: 'live',
},
{
id: 1,
liveStatus: 'live',
},
{
id: 4,
liveStatus: 'schedule',
}
]);
const displayBulletins = bulletins
.sort((a,b) => {
return a.liveStatus.localeCompare(b.liveStatus)
})
.map((bulletin) => {
return (
<Fragment key={bulletin.id}>
<BulletinList
bulletin={bulletin}
/>
</Fragment>
);
})
You can use indexOf to check the first letter in a list of sorted letters ('lse' in this case):
const bulletins = [{id: 1,liveStatus: 'live',},{id: 2,liveStatus: 'live',},{id: 3,liveStatus: 'end',},{id: 4,liveStatus: 'schedule',},{id: 5,liveStatus: 'end',}];
bulletins.sort((a, b) => 'lse'.indexOf(a.liveStatus[0]) - 'lse'.indexOf(b.liveStatus[0]));
console.log(bulletins);
Then no need to sort the elements, all you can use is reduce and arrange in the way you like
const arr = [
{
id: 1,
liveStatus: "live",
},
{
id: 2,
liveStatus: "live",
},
{
id: 3,
liveStatus: "end",
},
{
id: 4,
liveStatus: "schedule",
},
{
id: 5,
liveStatus: "end",
},
];
const dict = { live: 0, schedule: 1, end: 2 };
const resultObj = Array.from({ length: Object.keys(dict).length + 1 }, () => []);
const result = arr
.reduce((acc, curr) => {
acc[dict[curr.liveStatus] ?? resultObj.length - 1].push(curr);
return acc;
}, resultObj)
.flat();
console.log(result);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}

Conditional copying properties and values from one array of objects to another

Got two arrays of objects and my goal is to check if the value under property id from array1 matches the value under the property categoryId of array2. When find a match want to add the missing property amount to the relevant member of array1 or create a new array containing all the properties and values I need - id, name, amount
Here are the two arrays:
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}]
and
const array2 = [
{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},]
Tried to combine different approaches from answers to similar but simpler cases already posted in the community but didn't managed to make them work in my case.
Loop through each item in array1, then loop through each item in array2 inside the loop and check whether the categoryId is equal to the id.
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}
]
const array2 = [{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},
]
array1.forEach((e) => {
array2.forEach((f) => {
if (f.categoryId == e.id) {
e.amount = f.expence.amount;
}
})
})
console.log(array1);
You can also make use of Array.filter to find the item where the categoryId is equal to the id:
const array1 = [{
id: 8,
name: 'Online Shopping',
},
{
id: 12,
name: 'Subscriptions',
},
{
id: 5,
name: 'Patreon donations',
}
]
const array2 = [{
expence: {
amount: -66.66,
},
categoryId: 5,
},
{
expence: {
amount: 100018.85,
},
categoryId: 0,
},
{
expence: {
amount: -43340.9,
},
categoryId: 12,
},
]
array1.forEach((e) => {
var arr = array2.filter(f => f.categoryId == e.id);
if(arr.length > 0) e.amount = arr[0].expence.amount;
})
console.log(array1);

compare two arrays in javascript and delete the object that both arrays have

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));

Build Object Recursive

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>');

Categories