Relate and merge array of same Department - javascript

I am working on an application where I need to get combine the object of same department based on the
conditions provided in the second Array and attach the relation to the object.
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"}
]
Output
outArr ={
[{"ID":"1","NAME":"KEN","DEPT1":"CSE","REL":"AND"},
{"ID":"2","NAME":"MARK","DEPT2":"IT","REL":"AND"}], //Arr1
[{"ID":"3","NAME":"TOM","DEPT3":"ECE","REL":"OR"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB","REL":"OR"}], //Arr2
[{"ID":"5","NAME":"TIM","DEPT5":"SEC"}] //Arr3
}
Code:
let condArr=[],outArr,i=1;
inArr1.forEach(condt => {
let dept = Object.keys(condt)[0];
let tmparr = dept.split("D");
tmparr.shift()
condArr.push(tmparr)
});
inArr2.forEach(condt => {
if(condArr.includes(inArr2.D+i)){
i++;
outArr.push(inArr2);
}
});

Your code has a bit confused logic, i would suggest rather this
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"},{"D5D6":"AND"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"},
{"ID":"6","NAME":"TLA","DEPT6":"SEC"},
]
// first lets create object of ids as keys and conditions as values
const [keys, conditions] = inArr1.reduce((agg, cond, index) => {
Object.entries(cond).forEach(([key, value]) => {
key.split('D').forEach(v => { if (v) agg[0][v] = { value, index }})
agg[1].push([])
})
return agg
}, [{}, []]) // {1: "AND", 2: "AND", 3: "OR", 4: "OR"}
conditions.push([])
// and now just map over all elements and add condition if we found id from the keys
inArr2.forEach(item => {
const cond = keys[item.ID]
if (cond) conditions[cond.index].push({...item, REL: cond.value})
else conditions[conditions.length - 1].push(item)
})
const res = conditions.filter(v => v.length)
console.log(res)

You could store the goups by using the ID and use new objects.
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }],
inArr2 = [{ ID: "1", NAME: "KEN", DEPT1: "CSE" }, { ID: "2", NAME: "MARK", DEPT2: "IT" }, { ID: "3", NAME: "TOM", DEPT3: "ECE" }, { ID: "4", NAME: "SHIV", DEPT4: "LIB" }, { ID: "5", NAME: "TIM", DEPT5: "SEC" }],
groups = inArr1.reduce((r, o) => {
Object.entries(o).forEach(([k, REL]) => {
var object = { REL, group: [] };
k.match(/[^D]+/g).forEach(id => r[id] = object);
});
return r;
}, {}),
grouped = inArr2.reduce((r, o) => {
var { REL, group } = groups[o.ID] || {};
if (group) {
if (!group.length) r.push(group);
group.push(Object.assign({}, o, { REL }));
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

can try other solution:
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }, { D6D7: "XOR" }];
let inArr2 = [
{ ID: "1", NAME: "KEN", DEPT1: "CSE" },
{ ID: "2", NAME: "MARK", DEPT2: "IT" },
{ ID: "3", NAME: "TOM", DEPT3: "ECE" },
{ ID: "4", NAME: "SHIV", DEPT4: "LIB" },
{ ID: "5", NAME: "TIM", DEPT5: "SEC" },
{ ID: "9", NAME: "BAR", DEPT5: "XYZ" },
{ ID: "6", NAME: "FOO", DEPT5: "XYZ" },
];
let unmatchedArr = []
let matchedArr = inArr2.reduce((acc, obj) => {
// getting index matched from inArr1 objects key
const indexMatched = getIndexMatch(obj.ID);
// creating index if not exists
if (!acc[indexMatched] && indexMatched !== null) acc[indexMatched] = [];
// if some index matched it merge current obj with DEL property with inArr1[indexMatched] key => value
return indexMatched !== null
? acc[indexMatched].push({
...obj,
DEL: inArr1[indexMatched][Object.keys(inArr1[indexMatched])[0]]
})
// pushing on unmatchedArr
: unmatchedArr.push(obj)
, acc
}, []);
function getIndexMatch(id) {
for (const [index, obj] of inArr1.entries()) {
for (const key of Object.keys(obj)) {
// spliting only digits of the current key of object
if (key.match(/\d/g).includes(id)) return index; // returning index of inArr1 if is included
}
}
return null;
}
// merging arrays
const result = [...matchedArr, unmatchedArr];
console.log(result);

Related

If object set has an object property create an array

I have a response value which is dynamic which i need to store in redux state,
Response consist of array of object and and name
ex :
{data:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
{name:"def",age:"15",id:"20"}
]
name: "abc"
}
So if the name is same I need to create array with the name.
Expected :
abc:[
{name:"abc",age:"10",id:"10"}
{name:"abc",age:"15",id:"20"}
]
something I tried
data.map(function(o) {
if(data.name ==o.name)
return name[o];
});
If you're wanting a new object with a key of the name property you could try something like this
const response = {
data: [{
name: "abc",
age: "10",
id: "10"
},
{
name: "abc",
age: "15",
id: "20"
},
{
name: "def",
age: "15",
id: "20"
},
],
name: "abc"
}
const createSet = (someData) => {
let key = someData.name
let data = someData.data.filter(e => e.name === key)
return {
[key]: data
}
}
console.log(createSet(response))
You can extract duplicated using reduce and filter :
var data = {
data:[
{name:"abc",age:"10",id:"10"},
{name:"abc",age:"15",id:"20"},
{name:"def",age:"15",id:"20"}
],
name: "abc"
}
const lookup = data.data.reduce((a, e) => {
a[e.name] = ++a[e.name] || 0;
return a;
}, {});
console.log(data.data.filter(e => lookup[e.name]));

How to filter in two deep arrays

I'm looking to filter in two deep arrays, actually my JSON:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
I would like to get something like that when I filter with the word "ric":
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
}]
}
}
But I got this result:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
}
}
My code:
dataSort.categories = the json and
event.target.value.toLowerCase() = the specific word
dataSort.categories.filter(s => s.products.find(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())));
You can achieve this with a combination of reduce and filter
var input = {
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
var search = "ric"
var result = Object.entries(input).reduce( (acc, [key,val]) => {
found = val.product.filter(x => x.name.toLowerCase().includes(search.toLowerCase()))
if(found.length){
acc[key] = {...val, product: found}
}
return acc
},{})
console.log(result)
There is many approach to do this, one is to map your top level array to the subArrays filtered results then filter it after:
dataSort.categories
.map(s => s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())))
.filter(s => !!s.products.length);
You may also prefer to get a "flat" array as result because it is easier to use after :
dataSort.categories
.reduce((acc, s) => [...acc, s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase()))], []);
Please find below the code to filter out values inside the product.name and only return the value which are matching the equality condition in product array.
const json = [
{
product: [
{
uuid: "uid",
name: "Rice",
},
{
uuid: "uid",
name: "Pasta",
},
],
},
{
product: [
{
uuid: "uid",
name: "Milk",
},
],
},
];
const inputValue = "rIc";
const filteredArray = [];
json.map((s) => {
const item = s.product.find((p) =>
p.name.toLowerCase().includes(inputValue.toLowerCase())
);
item && filteredArray.push({ product: item });
});
console.dir(filteredArray);
Your dataset is an Object, not an Array and the filter is an Array method. You can use reduce by looping on the object values by Object.values then filter your products array.
const data = {
'0': {
product: [
{
uuid: 'uid',
name: 'Rice',
},
{
uuid: 'uid',
name: 'Pasta',
},
],
},
'1': {
product: [
{
uuid: 'uid',
name: 'Milk',
},
],
},
};
const keyword = 'ric';
const dataset = Object.values(data);
const results = dataset.reduce((acc, item, index) => {
const search = keyword.toLowerCase();
const product = item.product.filter(product => product.name.toLowerCase().includes(search));
if (product.length) acc[index] = { ...item, product };
return acc;
}, {});
console.log(results);

Change particular key in nested array to object in javascript

If the fields key in a object is array, change the first value of arrays as a key value pair object in javascript.
var obj =
[
{ id:1, fields:["item", "2", "list"]},
{ id:2, fields:["sample", "1", "check"]}
]
function newObj(obj) {
let objFields = {};
modifiedObj.fields.forEach(field => objFields[field] = field);
modifiedObj.fields= objFields;
return modifiedObj;
}
var result = this.newObject(obj)
Expected Output
{
item: "item",
sample: "sample"
}
Try this:
var obj =
[
{ id:1, fields:["item", "2", "list"]},
{ id:2, fields:["sample", "1", "check"]}
]
function newObject(obj) {
let objFields = {};
obj.forEach(e => {
if(e.fields && e.fields.length>0)
objFields[e.fields[0]] = e.fields[0];
});
return objFields;
}
var result = this.newObject(obj);
console.log(result);
Here is a functional approach that makes use of Object.assign(), spread operator, and Array.map() to create the object you need.
const input = [
{ id: 1, fields: ["item", "2", "list"] },
{ id: 2, fields: ["sample", "1", "check"] }
];
const process = (input) => (Object.assign(...input.map(({ fields }) => (
fields.length ? { [fields[0]]: fields[0] } : {}
))));
console.log(process(input));
Your snippet was close, you just needed to clean up the variable names, and then using map makes it a bit neater too:
const obj = [
{id: 1, fields: ["item", "2", "list"]},
{id: 2, fields: ["sample", "1", "check"]}
]
function newObj(inputArray) {
let outputObject = {};
inputArray.map(item => item.fields[0])
.forEach(field => outputObject[field] = field);
return outputObject;
}
var result = newObj(obj)
console.log(result)

How to create the object based on id and merge the inner key into an array JavaScript

I have a list of product each list have product code, parent id and product name. when I click product I am pushing into an array of an object listed below.
[
{"pid":"1","pcode":"van","pname":"mobile"},
{"pid":"1","pcode":"van","pname":"hphone"},
{"pid":"2","pcode":"car","pname":"wphone"},
{"pid":"2","pcode":"car","pname":"email"},
{"pid":"4","pcode":"bus","pname":"sms"}
]
how to create the object group based on id and merge the key3 into an array.
{
"pid":"1",
"details":[
{
"pcode":"van",
"pname":["mobile","hphone"]
}
]
},
{
"pid":"2",
"details":[
{
"pcode":"car",
"pname":["wphone","email"]
}
]
},
{
"pid":"3",
"details":[
{
"pcode":"bus",
"pname":["sms"]
}
]
}
I would use .reduce() for this scenario thus inside you can use .find() to create the desired output. The last pid for bus should be 4 instead of 3 anyway based on the listed array.
Try the following:
const data = [ {"pid":"1","pcode":"van","pname":"mobile"},{"pid":"1","pcode":"van","pname":"hphone"},{"pid":"2","pcode":"car","pname":"wphone"},{"pid":"2","pcode":"car","pname":"email"}, {"pid":"4","pcode":"bus","pname":"sms"}];
const result = data.reduce((a, c) => {
const found = a.find(e => e.pid === c.pid);
if (found) found.details[0].pname.push(c.pname);
else a.push({ pid: c.pid, details: [
{ pcode: c.pcode, pname: [ c.pname ] }
]});
return a;
}, []);
console.log(result);
I hope this helps!
Use .reduce can be done very easily and one for in loop to collect
result. faster result
const data = [
{ pid: "1", pcode: "van", pname: "mobile" },
{ pid: "1", pcode: "something", pname: "hphone" },
{ pid: "1", pcode: "van", pname: "hphone" },
{ pid: "2", pcode: "car", pname: "wphone" },
{ pid: "2", pcode: "car", pname: "email" },
{ pid: "4", pcode: "bus", pname: "sms" }
];
let result = data.reduce((map, cur) => {
if (!map[cur.pid]) {
map[cur.pid] = {
pid: cur.pid,
details: []
}
}
let hasMatch = false
map[cur.pid].details.forEach(x => {
if (x.pcode == cur.pcode) {
hasMatch = true
x.pname.push(cur.pname)
}
})
if (!hasMatch) {
map[cur.pid].details.push({
pcode: cur.pcode,
pname: [cur.pname]
})
}
return map
}, {})
let finalResult = []
for (const r in result) {
finalResult.push(result[r])
}
console.log(JSON.stringify(finalResult, null, 4));
It is possbible to use reduce method to create grouped array by pid. Then we can use map method to assign sequantial pid:
const obj = arr.reduce((a, {pid, pcode, pname}) => {
a[pid] = a[pid] || {pid, details: []};
if (a[pid].details.length === 0)
a[pid].details.push({pcode, pname:[pname]});
else
a[pid].details[0].pname.push(pname);
return a;
}, {})
const result = Object.values(obj).map((v, index) => ({...v, pid: ++index,}))
console.log(result);
An example:
let arr = [
{"pid":"1","pcode":"van","pname":"mobile"},
{"pid":"1","pcode":"van","pname":"hphone"},
{"pid":"2","pcode":"car","pname":"wphone"},
{"pid":"2","pcode":"car","pname":"email"},
{"pid":"4","pcode":"bus","pname":"sms"}
];
const obj = arr.reduce((a, {pid, pcode, pname}) => {
a[pid] = a[pid] || {pid, details: []};
if (a[pid].details.length === 0)
a[pid].details.push({pcode, pname:[pname]});
else
a[pid].details[0].pname.push(pname);
return a;
}, {})
const result = Object.values(obj).map((v, index) => ({...v, pid: ++index,}))
console.log(result);

Convert paths with items to tree object

I'm trying to convert an array of object contains paths with item to tree of data so I wrote a function path loop on the path:
From this array:
[
{ userName: "1", tags: ["A;B"] },
{ userName: "2", tags: ["A;B"] },
{ userName: "3", tags: ["A;"] },
{ userName: "4", tags: ["A;B;C"] },
{ userName: "5", tags: ["A;B"] },
{ userName: "6", tags: ["A;B;C;D"] }
]
to this structure:
[{
name: "A",
families: [{
name: "B",
families: [{
name: "C",
families: [{
name: "D",
families: [],
items: ["6"]
}],
items: ["4"]
}],
items: ["1", "2", "5"]
}],
items: ["3"]
}]
function convertListToTree(associationList) {
let tree = [];
for (let i = 0; i < associationList.length; i++) {
let path = associationList[i].tags[0].split(';');
let assetName = associationList[i].userName;
let currentLevel = tree;
for (let j = 0; j < path.length; j++) {
let familyName = path[j];
let existingPath = findWhere(currentLevel, 'name', familyName);
if (existingPath) {
if (j === path.length - 1) {
existingPath.items.push(assetName);
}
currentLevel = existingPath.families;
} else {
let assets = [];
if (j === path.length - 1) {
assets.push(assetName)
}
let newPart = {
name: familyName,
families: [],
items: assets,
};
currentLevel.push(newPart);
currentLevel = newPart.families;
}
}
}
return tree;
}
function findWhere(array, key, value) {
let t = 0;
while (t < array.length && array[t][key] !== value) {
t++;
}
if (t < array.length) {
return array[t]
} else {
return false;
}
}
But I have some issue here that the expected output is not like I want
[
{
"name": "A",
"families": [
{
"name": "B",
"families": [
{
"name": "C",
"families": [
{
"name": "D",
"families": [],
"items": [
"6"
]
}
],
"items": [
"4"
]
}
],
"items": [
"1",
"2",
"5"
]
},
{
"name": "",
"families": [],
"items": [
"3"
]
}
],
"items": []
}
]
Can someone please help me to fix that
You should be able to use recursion to achieve this, using getFamilies and getUsers functions called at each level:
const allTags = ["A", "B", "C", "D"];
let a = [ { "userName": "1", "tags": ["A;B"] }, { "userName": "2", "tags": ["A;B"] }, { "userName": "3", "tags": ["A;"] }, { "userName": "4", "tags": ["A;B;C"] }, { "userName": "5", "tags": ["A;B"] }, { "userName": "6", "tags": ["A;B;C;D"] } ];
// This function assumes order is not important, if it is, remove the sort() calls.
function arraysEqual(a1, a2) {
return a1.length === a2.length && a1.sort().every(function(value, index) { return value === a2.sort()[index]});
}
function getUserNames(tags, arr) {
return arr.filter(v => arraysEqual(v.tags[0].split(';').filter(a => a),tags)).map(({userName}) => userName);
}
function getFamilies(tags) {
if (tags.length >= allTags.length) return [];
const name = allTags[tags.length];
const path = [...tags, name];
return [{ name, families: getFamilies(path), items: getUserNames(path, a)}];
}
let res = getFamilies([]);
console.log('Result:', JSON.stringify(res, null, 4));
The idea here is to iterate the data (the reduce loop), and whenever a node is missing from the Map (nodesMap), use createBranch to recursively create the node, create the parent (if needed...), and then assign the node to the parent, and so on. The last step is to get a unique list of root paths (A in your data), and extract them from the Map (tree) to an array.
const createBranch = ([name, ...tagsList], nodesMap, node) => {
if(!nodesMap.has(name)) { // create node if not in the Map
const node = { name, families: [], items: [] };
nodesMap.set(name, node);
// if not root of branch create the parent...
if(tagsList.length) createBranch(tagsList, nodesMap, node);
};
// if a parent assign the child to the parent's families
if(node) nodesMap.get(name).families.push(node);
};
const createTree = data => {
const tree = data.reduce((nodesMap, { userName: item, tags: [tags] }) => {
const tagsList = tags.match(/[^;]+/g).reverse(); // get all nodes in branch and reverse
const name = tagsList[0]; // get the leaf
if(!nodesMap.has(name)) createBranch(tagsList, nodesMap); // if the leaf doesn't exist create the entire branch
nodesMap.get(name).items.push(item); // assign the item to the leaf's items
return nodesMap;
}, new Map());
// get a list of uniqnue roots
const roots = [...new Set(data.map(({ tags: [tags] }) => tags.split(';')[0]))];
return roots.map(root => tree.get(root)); // get an array of root nodes
}
const data = [{"userName":"1","tags":["A;B"]},{"userName":"2","tags":["A;B"]},{"userName":"3","tags":["A;"]},{"userName":"4","tags":["A;B;C"]},{"userName":"5","tags":["A;B"]},{"userName":"6","tags":["A;B;C;D"]}];
const result = createTree(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Allow me to make two small changes, and ramda's mergeDeepWithKey will do most of the work for you.
Changes, before we start:
Make tags an array rather than an array containing one string (i.e. tags[0].split(";"))
Allow families to be a dictionary-like object rather than an array (if you ever need your array format, it's Object.values(dict))
Solution:
Transform every entry to a path of the desired format using reduce
Merge all paths with custom logic:
When merging name entries, don't change the name
When merging items entries, concatenate
const inp = [
{ userName: "1", tags: ["A","B"] },
{ userName: "2", tags: ["A","B"] },
{ userName: "3", tags: ["A"] },
{ userName: "4", tags: ["A","B","C"] },
{ userName: "5", tags: ["A","B"] },
{ userName: "6", tags: ["A","B","C","D"] }
];
// Transform an input element to a nested path of the right format
const Path = ({ userName, tags }) => tags
.slice(0, -1)
.reduceRight(
(families, name) => ({ name, families: { [families.name]: families },
items: []
}),
({ name: last(tags), families: {}, items: [userName] })
);
// When merging path entries, use this custom logic
const mergePathEntry = (k, v1, v2) =>
k === "name" ? v1 :
k === "items" ? v1.concat(v2) :
null;
const result = inp
.map(Path)
// Watch out for inp.length < 2
.reduce(
mergeDeepWithKey(mergePathEntry)
)
console.log(JSON.stringify(result, null, 2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const { mergeDeepWithKey, last } = R;</script>

Categories

Resources