Add complex array of objects into another object - javascript

I have an source object obj that looks like this and an array input
const obj = {
name: "xyz",
filter: {
and: [
{
or: [
{
and: []
}
]
}
]
}
};
const input = ["test1\name1", "test2\name2"]
I need to push objects that are formed after spiltting input by \. After splitting, using left side of the string i need to form an object like this
{ type: "type1", value: whatever the left hand value}
Same for right side value
{ type: "type2", value: whatever the right hand value}
And these objects should be pushed to innermost and in the source object.
Expected output
{
name: "xyz",
filter: {
and: [
{
or: [
{
and: [
{ type: "type1", value: "test1" },
{ type: "type2", value: "name1" },
{ type: "type1", value: "test2" },
{ type: "type2", value: "name2" }
]
}
]
}
]
}
}
Code that I tried
function processResult(input) {
return {
name: "xyz",
filter: {
and: [
{
or: [
{
and: getUpdatedValues(input)
}
]
}
]
}
};
}
// I need the getUpdateValues to be processing the each item from the input array and then sending the two objects back after splitting
function getUpdatedValues(input){
const updated = input.map(item => {
const spilt = item.split("\\");
});
}

Assuming that the input array would include an escape character and could be like so: ["test1\\name1", "test2\\name2"], presented below is one possible way to achieve the desired objective.
Code Snippet
const transformMyArr = (myArr) => (
myArr.flatMap(
s => {
const [leftie, rightie] = s.split('\\');
return ([{
type: 'type1', value: leftie
}, {
type: 'type2', value: rightie
}]);
}
)
);
/* code explanation
// method to transform the array to required format
const transformMyArr = (myArr) => (
myArr.flatMap( // iterate over the array and remove nested-array in result
s => { // manipulate each array element
// "split" using "\\" and store the left-side as "type"
// and the rest as "value"
const [type, value] = s.split('\\');
// explicit return of an array with two objects per array elt
return ([{
type: 'type1', value: leftie
}, {
type: 'type2', value: rightie
}]);
}
) // implicit return from the "transformMyArr" method
);
*/
let myInputArr = ["test1\\name1", "test2\\name2"];
const myObj = {
name: "test",
filter: {
and: [{
or: [{
and: [...transformMyArr(myInputArr) ]
}]
}]
}
};
console.log('updated obj:\n', myObj);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
EDIT
the left and right side value after splitting can be present in different items in the array too. How can I have only unique type1 , type2 objects inside final array
const myTransform2 = arr => {
// set-up empty arrays to hold left & right side elements
let leftEltArr = [], rightEltArr = [];
// iterate over the arg-array using ".forEach()"
arr?.forEach(
s => {
// split using "\\" to store left & right side elts
const [leftElt, rightElt] = s.split('\\');
// push elements into respective arrays
leftEltArr.push(leftElt);
rightEltArr.push(rightElt);
}
);
// return the result using left & right arrays
return (
([...new Set(leftEltArr)]) // remove dupes
.map(value => ({ type: 'type1', value })) // transform to required format
.concat( // concat result of similar operation on right-side
([...new Set(rightEltArr)])
.map(value => ({ type: 'type2', value }))
)
);
};
// updated sample input with 3rd elt which has duplicates
// on both left-side & right-side of the "\\"
let myInputArr = ["test1\\name1", "test2\\name2", "test1\\name2"];
const myObj = {
name: "test",
filter: {
and: [{
or: [{
and: [...myTransform2(myInputArr) ]
}]
}]
}
};
console.log('transformed object:\n', myObj);
.as-console-wrapper { max-height: 100% !important; top: 0 }

One way to do it. It's tricky because the input structure is so different to the output, and there is no reference for "type1"/"type2" other than the array element position.
const input = ["test1\\name1", "test2\\name2"];
function processResult(input) {
return {
name: "xyz",
filter: {
and: [
{
or: [
{
and: getUpdatedValues(input)
}
]
}
]
}
};
}
function getUpdatedValues(input){
return input.flatMap((item, i) => item.split("\\").map(val => ({[`type${i + 1}`]: val })));
}
console.log(processResult(input));

Related

Retain array structure when filtering nested array

My brain froze with this advanced filtering. This task has exceeded my basic knowledge of filter, map etc.
Here I have an array with nested objects with array:
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
You may have seen this sort of style if you've worked with React Native (RN). This question is not for RN. I need to perform a filter on the name property in the nested array and when I get a match, I must return the format as the DATA variable.
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
console.log(results);
};
My limited knowledge of deep filtering returns the basic filtering for the data array but need to retain the structure for DATA. The expected results I'd expect:
// I'm now querying for "ZAMASU"
const handleFiltering = (value='ZAMA') => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
// console.log(results) should now be
// [
// {
// title: 'Dragon Balls Z',
// data: [
// { id: 2, name: 'Zamasu' }
// ]
// }
// ];
};
What comes to mind is the use of {...DATA, something-here } but my brain has frozen as I need to get back the title property. How to achieve this, please?
Another solution would be first use filter to find only objects containing the name in data passed through the argument, subsequently mapping data.
Here is your adjusted filter method
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.filter((obj) =>
obj.data.some((character) => character.name.toLowerCase() === _value)
).map((obj) => ({
title: obj.title,
data: obj.data.filter(
(character) => character.name.toLowerCase() === _value
),
}));
console.log(results);
};
You can use reduce method of array. First find out the object inside data array and then add that to accumulator array as new entry by preserving the original structure.
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs', where: 'tv' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
let handleFiltering = (value='tv') => {
return DATA.reduce((acc,d) => {
let obj = d.data.find(a => a.name?.toLowerCase().includes(value.toLowerCase())
|| a.where?.toLowerCase().includes(value.toLowerCase()));
obj ? acc.push({...d, data:[obj]}) : null;
return acc;
}, []);
}
let result = handleFiltering();
console.log(result);

Sort an array of objects dynamically javascript

I am having an array with the below items. I need to sort the below array to the array that is shown in sorted items,so that all the value with the rules can be together and the ELIG_DATABASE should be grouped with the ELIG_SERVICE.
const items =[{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false},
{"name":"RULES_VERSION","ready":true},];
I want to achieve this array so that values in the name property that has rules can be together,gateway things should be together, elig service thing should be together just that ELIG_DATABASE should be grouped together with elig service and then all other values in the name property can be sorted alphabetically.
const sortedItems =[
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"RULES_VERSION","ready":true},
{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false}
];
I tried using this code but that sorts alphabetically putting ELIG_DATABASE in first position.Could any one please help on how to achieve this array in minimum code as possible.
items.sort((svcA, svcB) => {
const serviceA = svcA.name.toUpperCase();
const serviceB = svcB.name.toUpperCase();
return serviceA.localeCompare(serviceB);
});
You could take the wanted groups first in an array, sort the data and assign the object to the group or to the end of a temp array and get the flat data as result.
var data = [{ name: "ELIG_DATABASE", ready: true }, { name: "ELIG_RULES_SERVICE", ready: true }, { name: "ELIG_GATEWAY", ready: true }, { name: "ELIG_GATEWAY_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE", ready: true }, { name: "HDXTS", ready: false }, { name: "RULES_VERSION", ready: true }],
together = [['GATEWAY'], ['RULES'], ['ELIG_DATABASE', 'ELIG_SERVICE']],
groups = { GATEWAY: [], RULES: [], ELIG_DATABASE: [] },
temp = [groups.GATEWAY, groups.RULES, groups.ELIG_DATABASE],
result;
for (let o of data.sort(({ name: a }, { name: b }) => a.localeCompare(b))) {
let target = together.find(a => a.some(v => o.name.includes(v)));
if (target) groups[target[0]].push(o);
else temp.push(o);
}
result = temp.flat();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JavaScript: Filtering between the same nested key using lodash

I have an array baseTable, which looks like this:
baseTable = [
{
exid: "2",
name: "aa",
children_meta: {
has: false
}
},
{
exid: "1",
name: "aa1",
children_meta: {
has: false
}
},
{
exid: "3",
name: "bb",
children_meta: {
has: true
},
children: [
{
exid: "101",
name: "c101"
},
{
exid: "102",
name: "c102"
}
]
}
]
and another array againstTable like this:
againstTable = [
{
exid: "2",
name: "aa",
children_meta: {
has: false
}
},
{
exid: "3",
name: "bb",
children_meta: {
has: true
},
children: [
{
exid: "102",
name: "c102"
}
]
}
]
Is there a lodash method to select objects from the baseTable array where the same exid does not exist in the againstTable?
To illustrate, I need method that can produce the following array result from the two arrays above:
[
{
exid: "1",
name: "aa1",
children_meta: {
has: false
}
},
{
exid: "3",
name: "bb",
children_meta: {
has: true
},
children: [
{
exid: "101",
name: "c101"
}
]
}
]
This is how I was trying but this method becomes too big for a small task:
conditionalRender(o: { baseTable; againstTable }) {
const { baseTable, againstTable } = o;
// Check if there are no duplicates in the base
// table; check against, "against table"
// This could be possible after user performs a search
console.log(baseTable, "..base");
console.log(againstTable, "...againsr");
const baseMap = {};
const againstMap = {};
baseTable.forEach(row => (baseMap[row.pid] = row));
againstTable.forEach(row => (againstMap[row.pid] = row));
// const against_ids = new Set(againstTable.map(({ pid }) => pid));
// return baseTable.filter(({ pid }) => !against_ids.has(pid));
const filteredBaseTable: { [index: string]: any } = [];
baseTable.forEach(({ pid }) => {
if (baseMap[pid].children_meta.has) {
// If it is a group, check if there exists
// a part in another table
if (againstMap[pid]) {
// Opposite table also has the same eequipment group
// Will keep the children that are not present in the
// opposite table
// Each child can be differentiated by its exid
const exidsInAgainstTable = new Set(
againstMap[pid].children.map(crow => crow.exid)
);
// Keep only those ids in base table that do not exist in against table
const originalBaseChildren = baseMap[pid].children;
baseMap[pid].children = originalBaseChildren.filter(
({ exid }) => !exidsInAgainstTable.has(exid)
);
filteredBaseTable.push(baseMap[pid]);
}
} else {
if (!againstMap[pid]) {
filteredBaseTable.push(baseMap[pid]);
}
}
});
return filteredBaseTable;
}
This can be achieved without lodash using build-in array reduction.
For instance, you could call reduce on the baseTable array where for each iteration, you search for an item in againstTable that matches on exid.
If no match is found, add the baseItem to your output array (this represents the case where exid: "2" from your data above is added to the result).
If a match is found, examine the children sub arrays of both baseItem and againstItem (if present), and filter items in the baseItem.children array where that child's exid never occours in the againstItem.children sub-array. If the filtered result is non-empty, update the baseItem children array with the filtered result and add that to your output.
One way to express this is code would be:
const baseTable=[{exid:"2",name:"aa",children_meta:{has:false}},{exid:"1",name:"aa1",children_meta:{has:false}},{exid:"3",name:"bb",children_meta:{has:true},children:[{exid:"101",name:"c101"},{exid:"102",name:"c102"}]}];const againstTable=[{exid:"2",name:"aa",children_meta:{has:false}},{exid:"3",name:"bb",children_meta:{has:true},children:[{exid:"102",name:"c102"}]}];
const result = baseTable.reduce((output, baseItem) => {
const matchOnExid = againstTable.find(againstItem => {
return againstItem.exid === baseItem.exid;
});
if (matchOnExid) {
/* If match of exid found from agaistTable for current baseTable item
then examine the children sub-arrays */
const baseChildren = baseItem.children;
const againstChildren = matchOnExid.children;
if (Array.isArray(baseChildren) && Array.isArray(againstChildren)) {
/* If valid children sub-arrays exist of items, filter a subset of the
baseItem children for items that do not exist in the children sub-array
of the matched againstItem */
const matchChildrenOnExid = baseChildren.filter(baseChildItem =>
{
return againstChildren.every(againstChildItem => {
return againstChildItem.exid !== baseChildItem.exid;
});
});
if (matchChildrenOnExid.length > 0) {
/* If a subset of children do exist, then baseItem can be added to
resulting array. Note also that we need to update the children array
of the returned result to reflect the subset that was just found */
output.push({ ...baseItem,
children: matchChildrenOnExid
});
}
}
} else {
/* If no match of exid found, just add the baseItem to the
result */
output.push(baseItem);
}
return output;
}, []);
console.log(result);

Tree Structure From Underscore Delimited Strings

Good day,
I need to convert strings as such:
Process1_Cat1_Cat2_Value1
Process1_Cat1_Cat2_Value2
Process2_Cat1_Cat2_Value1
into a nested array as such:
var d = [{
text: 'Process1',
children: [{
text: 'Cat1',
children: [{
text: 'Cat2',
children: [{
text: 'Value1'
},
{
text: 'Value2'
}]
}]
}]
},
{
text: 'Process2',
children: [{
text: 'Cat1',
children: [{
text: 'Cat2',
children: [{
text: 'Value1'
}]
}]
}]
},
];
The reason why I need to do this is to make use of a treeview to display my data:
https://www.npmjs.com/package/bootstrap-tree-view
I have looked at the following solution but was not able to get it working due to lowdash library throwing errors on the findWhere function:
Uncaught TypeError: _.findWhere is not a function
http://brandonclapp.com/arranging-an-array-of-flat-paths-into-a-json-tree-like-structure/
See below for the code:
function arrangeIntoTree(paths, cb) {
var tree = [];
// This example uses the underscore.js library.
_.each(paths, function(path) {
var pathParts = path.split('_');
pathParts.shift(); // Remove first blank element from the parts array.
var currentLevel = tree; // initialize currentLevel to root
_.each(pathParts, function(part) {
// check to see if the path already exists.
var existingPath = _.findWhere(currentLevel, {
name: part
});
if (existingPath) {
// The path to this item was already in the tree, so don't add it again.
// Set the current level to this path's children
currentLevel = existingPath.children;
} else {
var newPart = {
name: part,
children: [],
}
currentLevel.push(newPart);
currentLevel = newPart.children;
}
});
});
cb(tree);
}
arrangeIntoTree(paths, function(tree) {
console.log('tree: ', tree);
});
Any help will be appreciated!
You could use an iterative by looking for the text at the actual level. If not found create a new object. Return the children array for the next level until the most nested array. Then add the leaf object.
var data = ['Process1_Cat1_Cat2_Value1', 'Process1_Cat1_Cat2_Value2', 'Process2_Cat1_Cat2_Value1'],
result = data.reduce((r, s) => {
var keys = s.split('_'),
text = keys.pop();
keys
.reduce((q, text) => {
var temp = q.find(o => o.text === text);
if (!temp) {
q.push(temp = { text, children: [] });
}
return temp.children;
}, r)
.push({ text });
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How can I concat an array to an array that is inside an array of objects?

I am trying to concat an array to an array (productsCategories) inside an array of objects.
So, here's what the productCategories array looks like:
[
{
id: 123,
items: [ { Obj1 }, { Obj2 } ]
},
{
id:456,
items: [ { Obj1 }, { Obj2 } ]
}
]
I have some new array, like [ { Obj3 }, { Obj4 } ] that I want to concat to the productCategories for the object where id = 123.
So to do this,
I've first used lodash's find to find the correct object to update and used concat to join the two arrays:
let nextItems:any = find(productCategories, { id: payload.id });
nextItems = assign({}, nextItems, { items: nextItems.items.concat(payload.items)});
So, nextItems.items has the concatenated items array.
However, I am having trouble now adding this to productCategories array. I need to find the object where id is the same as nextItems.id and then set productCategories.items equal to nextItems.items.
What is the correct way to do this?
Find the index of the object that matches the nextItems.id in the productCategories and assign the new concatenated array to it. You can use the lodash findIndex() method to find the index of the object that matches the id.
var index = _findIndex(productCategories, { id: nextItems.id });
productCategories[index].items = nextItems.items;
You can use plain JavaScript just as well. With ES6 spread syntax it can look like this:
productCategories.filter(x => x.id == payload.id)
.forEach(x => x.items.push(...payload.items));
Here is a snippet with sample data:
// Sample data
var productCategories = [{
id: 123,
items: [ { a: 1 }, { a: 2 } ]
}, {
id: 456,
items: [ { b: 1 }, { b: 2 } ]
}];
var payload = {
id: 123,
items: [ { c: 1 }, { c: 2 } ]
};
// Update with payload
productCategories.filter(x => x.id == payload.id)
.forEach(x => x.items.push(...payload.items));
// Show results
console.log(productCategories);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories