Combining same objects in json array javascript - javascript

I have this array:
[{ "id": 1, "myId": "100", "name": "amey" }, { "id": 2, "myId": "100", "name": "anuj" }, { "id": 3, "myId": "101", "name": "suraj" }, { "id": 4, "myId": "101", "name": "suraj h" }]
I want output like this:
[{ "id": 1, "myId": "100", "name": ["amey", "anuj"] }, { "id": 3, "myId": "101", "name": ["suraj", "suraj h] }]
How can I do this using javascript
for (var i = 0; i < myarray.length; i++) {
//And loop again for duplicate data
for (var j = i + 1; j < myarray.length; j++) {
if (
myarray[i].VENDOR_ID == myarray[j].VENDOR_ID &&
myarray[i].ORDER_ID === myarray[j].ORDER_ID
) {
var tmp = myarray[j].NAME;
console.log(tmp);
myarray[j].NAME = [];
myarray[j].NAME.push(tmp);
myarray[j].NAME.push(myarray[i].NAME);
myarray[i] = {};
}
}
}

You can use an array reduce into an object and return the array of values. Reduce into an object using the myId property as the key to group by. Shallow copy any existing state and and name array, appending the new name value from the current element.
Object.values(
input.reduce(
(acc, { id, myId, name }) => ({
...acc,
[myId]: {
...(acc[myId] || { id, myId }),
name: [...(acc[myId]?.name || []), name]
}
}),
{}
)
const input = [
{ id: 1, myId: "100", name: "amey" },
{ id: 2, myId: "100", name: "anuj" },
{ id: 3, myId: "101", name: "suraj" },
{ id: 4, myId: "101", name: "suraj h" }
];
const res = Object.values(
input.reduce(
(acc, { id, myId, name }) => ({
...acc,
[myId]: {
...(acc[myId] || { id, myId }),
name: [...(acc[myId]?.name || []), name]
}
}),
{}
)
);
console.log(JSON.stringify(res));

You can use Array.prototype.reduce():
const arr1 = [{
"id": 1,
"myId": "100",
"name": "amey"
}, {
"id": 2,
"myId": "100",
"name": "anuj"
}, {
"id": 3,
"myId": "101",
"name": "suraj"
}, {
"id": 4,
"myId": "101",
"name": "suraj h"
}]
const reduced = arr1.reduce((acc, item) => {
// 1. check if the 'acc' array already contains an item with the same 'myId' attribute
const itemIndex = acc.findIndex(it => it.myId === item.myId);
// 2. if there isn't any, push into the 'acc' array a copy of the item,
// with the 'name' property converted into an array of strings
// otherwise simply push the 'name' into the already existing item
if (itemIndex === -1) {
acc.push({
...item,
name: [item.name]
});
} else {
acc[itemIndex].name.push(item.name)
}
return acc;
}, []);
// test
console.log(reduced);

Related

String-path to Tree (JavaScript)

I have an array of paths in string format like that:
[
{ _id: 'women/clothes/tops', count: 10 },
{ _id: 'women/clothes/suits', count: 5 },
{ _id: 'women/accessories', count: 2 },
{ _id: 'men/clothes', count: 1 },
]
I would like to group them into a tree structure like that:
[
{
_id: 'women',
count: 17,
children: [
{
_id: 'clothes',
count: 15,
children: [
{ _id: 'tops', count: 10 },
{ _id: 'suits', count: 5 }
]
},
{
_id: 'accessories',
count: 2
}
]
},
{
_id: 'men',
count: 1,
children: [
{
_id: 'clothes',
count: 1
}
]
}
]
I would imagine a sort of recursive function calling a reduce method. But I can't figure how exactly.
EDIT :
I managed to get close with this solution. But I still get an empty object key, and i cannot manage to not have the children key when there are no children:
const getTree = (array) => {
return array.reduce((a, b) => {
const items = b._id.replace('\/', '').split('/')
return construct(a, b.count, items)
}, {})
}
const construct = (a, count, items) => {
const key = items.shift()
if(!a[key]) {
a[key] = {
_id: key,
count: count,
children: []
}
a[key].children = items.length > 0 ? construct(a[key].children, count, items) : null
}
else {
a[key].count += count
a[key].children = items.length > 0 ? construct(a[key].children, count, items) : null
}
return a
}
I created an object tree first and then converted that to your array of objects with children structure.
Note: I used a _count property on each object in the intermediate structure so that when looping over the keys later (when creating the final structure), I could ignore both _id and _count easily, and loop over only the "real children" keys, which don't start with _.
I did not look at your current attempt/solution before writing this, so mine looks quite different.
const origData = [
{ _id: 'women/clothes/tops', count: 10 },
{ _id: 'women/clothes/suits', count: 5 },
{ _id: 'women/accessories', count: 2 },
{ _id: 'men/clothes', count: 1 },
];
const newObj = {};
for (let obj of origData) {
//console.log(obj)
const tempArr = obj._id.split('/');
let tempHead = newObj; // pointer
for (let idx in tempArr) {
let head = tempArr[idx];
if (!tempHead.hasOwnProperty(head)) {
tempHead[head] = {};
}
tempHead = tempHead[head];
tempHead._id = head;
const currCount = tempHead._count || 0;
tempHead._count = currCount + obj.count;
}
tempHead._count = obj.count;
}
console.log(newObj);
const finalArr = [];
let tempArrHead = finalArr; // pointer
let tempObjHead = newObj; // pointer
function recursiveStuff(currObj, currArr, copyObj) {
let hasChildren = false;
const keys = Object.keys(currObj).filter(a => !a.startsWith("_"));
for (let key of keys) {
hasChildren = true;
const obj = {
_id: currObj[key]._id,
count: currObj[key]._count || 0,
children: [],
};
currArr.push(obj);
recursiveStuff(currObj[key], obj.children, obj)
}
if (hasChildren == false) {
// console.log(copyObj);
// there might be a more elegant way, but this works:
delete copyObj.children;
}
}
recursiveStuff(tempObjHead, tempArrHead)
console.log(finalArr);
.as-console-wrapper{
max-height: 100% !important;
}
Intermediate Structure:
{
"women": {
"_id": "women",
"_count": 17,
"clothes": {
"_id": "clothes",
"_count": 15,
"tops": {
"_id": "tops",
"_count": 10
},
"suits": {
"_id": "suits",
"_count": 5
}
},
"accessories": {
"_id": "accessories",
"_count": 2
}
},
"men": {
"_id": "men",
"_count": 1,
"clothes": {
"_id": "clothes",
"_count": 1
}
}
}
Final Structure:
[
{
"_id": "women",
"count": 17,
"children": [
{
"_id": "clothes",
"count": 15,
"children": [
{"_id": "tops", "count": 10},
{"_id": "suits", "count": 5}
]
},
{"_id": "accessories", "count": 2}
]
},
{
"_id": "men",
"count": 1,
"children": [
{"_id": "clothes", "count": 1}
]
}
]

Remove JSON attributes if not found in defined array

I need to remove attribute from the meta if it's not exist in att
for example : cardNo is not existing in the att
const att = ['id', 'name','class'];
const meta = [
{
"id": 1,
"name": "test",
"cardNo": 23
},
{
"id": 2,
"name": "test2",
"cardNo": 232
}
];
Expected output:
[
{
"id": 1,
"name": "test",
},
{
"id": 2,
"name": "test2"
}
];
for(let data of meta){
for (let key of Object.keys(data)) {
if (arr.indexOf(key) == -1) {
delete obj[key];
}
} }
Use Ramda.js (https://ramdajs.com/docs/) to make it easier:
const att = ['id', 'name','class'];
const meta = [
{
"id": 1,
"name": "test",
"cardNo": 23
},
{
"id": 2,
"name": "test2",
"cardNo": 232
}
]
const onlyAtts = map(pick(att), meta)
i think this is the answer for your question.
const att = ['id', 'name','class'];
const meta = [
{
"id": 1,
"name": "test",
"cardNo": 23
},
{
"id": 2,
"name": "test2",
"cardNo": 232
}
];
let newMeta = meta.map((d) => {
let obj = {};
att.forEach((currentAtt) => {
if(d.hasOwnProperty(currentAtt)) {
obj[currentAtt] = d[currentAtt];
}
});
return obj;
});
console.log(newMeta);
You can easily achieve this using map and reduce.
const att = ["id", "name", "class"];
const meta = [
{
id: 1,
name: "test",
cardNo: 23,
},
{
id: 2,
name: "test2",
cardNo: 232,
},
];
const result = meta.map((obj) => {
const perfectObj = att.reduce((acc, curr) => {
if (obj[curr]) {
acc[curr] = obj[curr];
}
return acc;
}, {});
return perfectObj;
});
console.log(result);
This is also works
const att = ['id', 'name','class'];
const meta = [
{
"id": 1,
"name": "test",
"cardNo": 23
},
{
"id": 2,
"name": "test2",
"cardNo": 232
}
];
function compare(meta, fields) {
meta.map(object => {
let dataKeys = Object.keys(object);
dataKeys.forEach(element => {
if(fields.indexOf(element) < 0) {
delete object[element];
}
});
});
}
compare(meta, att);
console.log(meta);

Javascript array equality control and changing value

Hi I have two changeable length arrays and I tried If there is no value I want, delete it from that array and change the sum value if it has changed array 2 same serials code
array1 = [
{
"serial": "3",
"sum": "1"
},
{
"serial": "700",
"sum": "2"
},
{
"serial": "300",
"sum": "1"
},
]
array2 = [{
"someting": 10,
"sum": "3",
"serialList": ["700","711"],
},
{
"someting": 10,
"sum": "1",
"serialList": ["300"],
},
{
"someting": 10,
"sum": "2",
"serialList": [],
}
]
his my two array as I said arrays length is changeable sometimes array1 length big, sometimes array2 and I want If serial number in array1 does not exist in array2 delete from array1 element and change the sum value if it has changed array2 same serials code, according to above array1[0] serial codes does not exist and array1[1] sum value different array2[0] sum value change to sum value array1[1] to array2[0], serial number 300 to same sum number to array don't do anything I want to output array1 is:
array1 = [
{
"serial": "700",
"sum": "3"
},
{
"serial": "300",
"sum": "1"
},
]
Using a flatMap
array1.flatMap(el => {
// find array2 element with array1 element's serial
const array2el = array2.find(({ serialList }) =>
serialList.includes(el.serial)
);
if (array2el) {
if (array2el.sum !== el.sum) {
el.sum = array2el.sum; // sum different, update
}
} else {
return []; // return [] to delete
}
return [el]; // return [el] to keep
});
const array1 = [
{
serial: "3",
sum: "1"
},
{
serial: "700",
sum: "2"
},
{
serial: "300",
sum: "1"
}
];
const array2 = [
{
someting: 10,
sum: "3",
serialList: ["700", "711"]
},
{
someting: 10,
sum: "1",
serialList: ["300"]
},
{
someting: 10,
sum: "2",
serialList: []
}
];
const processedArray1 = array1.flatMap(el => {
const array2el = array2.find(({ serialList }) =>
serialList.includes(el.serial)
);
if (array2el) {
if (array2el.sum !== el.sum) {
el.sum = array2el.sum;
}
} else {
return []; // delete
}
return [el]; // return el
});
console.log(processedArray1);
Using a reduce
const processedArray1 = array1.reduce((acc, el) => {
// find array2 element with array1 element's serial
const array2el = array2.find(({ serialList }) =>
serialList.includes(el.serial)
);
if (array2el) {
if (array2el.sum !== el.sum) {
el.sum = array2el.sum; // sum different, update
}
acc.push(el); // push into filtered array if found in array2
}
return acc;
}, []);
const array1 = [
{
serial: "3",
sum: "1"
},
{
serial: "700",
sum: "2"
},
{
serial: "300",
sum: "1"
}
];
const array2 = [
{
someting: 10,
sum: "3",
serialList: ["700", "711"]
},
{
someting: 10,
sum: "1",
serialList: ["300"]
},
{
someting: 10,
sum: "2",
serialList: []
}
];
const processedArray1 = array1.reduce((acc, el) => {
const array2el = array2.find(({ serialList }) =>
serialList.includes(el.serial)
);
if (array2el) {
if (array2el.sum !== el.sum) {
el.sum = array2el.sum;
}
acc.push(el)
}
return acc;
}, []);
console.log(processedArray1);

Constructing Tree View Object

I'd like to construct an Array Object for tree view in React Native.
Realm DB returns following rows as they have parent-child relation:
{
"0":{
"ID":3,
"name":"KITCHEN",
"parentID":2,
"createdBY":null,
"createdAT":null
},
"1":{
"ID":4,
"name":"BATHROOM",
"parentID":2,
"createdBY":null,
"createdAT":null
},
"2":{
"ID":5,
"name":"OIL",
"parentID":3,
"createdBY":null,
"createdAT":null
},
"3":{
"ID":6,
"name":"LIQUID",
"parentID":5,
"createdBY":null,
"createdAT":null
},
"4":{
"ID":7,
"name":"SOLID",
"parentID":5,
"createdBY":null,
"createdAT":null
}
}
Object should be look like this:
const treeData = [
{
key: 3,
label: 'KITCHEN',
nodes: [
{
key: '5',
label: 'OIL',
nodes: [
{
key: '6',
label: 'LIQUID',
},
{
key: '7',
label: 'SOLID',
},
],
},
],
},
{
key: 4,
label: 'BATHROOM',
},
];
My attempt was looping over all rows and get their IDs then in a nested loop checking the parentID with the ID and if any match occurs then adding that node to another object.
This only gives me the child/s of any parent.
for (var i = 0; i < rows.length; i++) {
let tempID = rows[i].ID
treeData = treeData.concat(rows[i])
for (var j = 0; j < rows.length; j++) {
let tempParentID = rows[j].parentID
if (tempID == tempParentID) {
subCategoryJson = subCategoryJson.concat(rows[j])
}
}
}
Problem is I am really not sure how to construct exactly the above Array Object.
PS. I'm trying to use following node module: https://www.npmjs.com/package/react-simple-tree-menu
You could store the keys and filter the parentt nodes for the result.
var data = { 0: { ID: 3, name: "KITCHEN", parentID: 2, createdBY: null, createdAT: null }, 1: { ID: 4, name: "BATHROOM", parentID: 2, createdBY: null, createdAT: null }, 2: { ID: 5, name: "OIL", parentID: 3, createdBY: null, createdAT: null }, 3: { ID: 6, name: "LIQUID", parentID: 5, createdBY: null, createdAT: null }, 4: { ID: 7, name: "SOLID", parentID: 5, createdBY: null, createdAT: null } },
tree = function (data) {
var t = {},
parents = {};
Object.values(data).forEach(({ ID: key, name: label, parentID }) => {
Object.assign(t[key] = t[key] || {}, { key, label });
t[parentID] = t[parentID] || { };
t[parentID].nodes = t[parentID].nodes || [];
t[parentID].nodes.push(t[key]);
parents[key] = true;
});
return Object
.keys(t)
.filter(k => !parents[k])
.flatMap(k => t[k].nodes);
}(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would first loop and create a look up object so it is easy to reference parents and check if a parent exists.
After that I would loop over the data again and check to see if it has a parent, if it does, add a nodes property and push the element to it. If not add to the parent node.
const data = {
"0": {
"ID": 3,
"name": "KITCHEN",
"parentID": 2,
"createdBY": null,
"createdAT": null
},
"1": {
"ID": 4,
"name": "BATHROOM",
"parentID": 2,
"createdBY": null,
"createdAT": null
},
"2": {
"ID": 5,
"name": "OIL",
"parentID": 3,
"createdBY": null,
"createdAT": null
},
"3": {
"ID": 6,
"name": "LIQUID",
"parentID": 5,
"createdBY": null,
"createdAT": null
},
"4": {
"ID": 7,
"name": "SOLID",
"parentID": 5,
"createdBY": null,
"createdAT": null
}
}
const values = Object.values(data)
const lookup = values.reduce((obj, entry) => ({
[entry.ID]: entry,
...obj
}), {})
const result = values.reduce((arr, entry) => {
const parent = lookup[entry.parentID]
if (parent) {
parent.nodes = parent.nodes || []
parent.nodes.push(entry)
} else {
arr.push(entry)
}
return arr
}, [])
console.log(result)

Mutate or Make a new Array by replacing a value

I have two arrays (and the length can be in 1000s):
I want my array to replace status to the status of array 2. Here is the output example:
[{
value: 123,
status: 'demo',
type: '...'
},
{value: 2335,
status: 'demo2',
type: 'xxx'
}]
As we can see it needs to get the status from another array and replace it. What are the most possible efficient solutions for this? As this array can be very large. I don't know a good approach to solve this problem.
Length and sort order can be different, I need to replace array1's status by the array2's status,
By linking Array1's status and Array2's id
My actual Data
[
{
"id": "55",
"status": "2",
"type": "COD",
"value": "5038.2",
},
{
"id": "56",
"status": "2",
"type": "COD",
"value": "5398.2",
},
{
"id": "57",
"status": "2",
"type": "COD",
"value": "10798.2",
}
]
Array 2
[
{
"id": "1",
"status": "Awaiting Confirmation",
},
{
"id": "2",
"status": "Confirmed",
},
{
"id": "3",
"status": "Awaiting Shipment",
},
{
"id": "4",
"status": "Awaiting Pickup",
},
{
"id": "5",
"status": "Shipped",
},
{
"id": "6",
"status": "Delivered",
},
{
"id": "7",
"status": "Cancelled",
},
{
"id": "8",
"status": "Refund Requested",
},
{
"id": "9",
"status": "Refunded",
}
Things i have tried...I have used lodash and a for loop to achieve this
const output = [];
for (let i = 0; i < array1.length; i++) {
const statuscode = array1[i].status;
const result = _.find(array2, { id: statuscode });
output.push({
value: array1[i].value,
status: result.status,
type: array1[i].type
});
}
console.log(output);
For high performance, transform one of the arrays to a Map first. Map lookups are very efficient:
const input1 = [{
value: 123,
status: 1,
type: 'COD',
},
{
value: 2335,
status: 2,
type: 'COD',
},
{
value: 222,
status: 3,
type: 'COD',
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map2 = new Map(Object.values(input2).map(({ id, status }) => [id, status]));
const output = input1.map(({ status, ...rest }) => {
const otherStatus = map2.get(status);
return { ...rest, status: otherStatus };
});
console.log(output);
Code readability generally matters more than speed, but if you wanted, you could transform the .map transformation into a for loop as well:
const input1 = [{
value: 123,
status: 1
},
{
value: 2335,
status: 2
},
{
value: 222,
status: 3
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map1 = new Map(Object.values(input1).map(({ value, status }) => [status, value]));
const output = [];
for (let i = 0; i < input2.length; i++) {
const { id, status } = input2[i];
output.push({ value: map1.get(id), status });
}
console.log(output);
A simple for loop would do:
for (let i = 0; i < array1.length; i++) {
array1[i].status = array2[i].status;
}
This of course assumes that the length and the order of the two arrays is the same.
EDIT
Alternative solution using Array.prototype.find and taking into account different lengths and orders.
for (let i = 0; i < array1.length; i++) {
const buffer = array1[i];
buffer.status = array2.find(x => x.id === buffer.status).status;
}
Also, I would highly recommend giving priority to readability over premature optimisation

Categories

Resources