I already figured out the way to grouping object by its category using the code below:
let groupBy = (element, key) => {
return element.reduce((value, x) => {
(value[x[key]] = value[x[key]] || []).push(x);
return value;
}, {});
};
let items = groupBy(results, 'category')
And, the result would be like so:
{
"Administration": [
{
"shp_name": "Village Boundary",
"color_prop": "#000000",
"shp_prop": "Batu Ampar"
},
{
"shp_name": "Village Boundary",
"color_prop": "#FFFFFF",
"shp_prop": "Sungai Jawi"
}
],
"Land_use": [
{
"shp_name": "Land Use 2019",
"color_prop": "#000000",
"shp_prop": "Grassland"
},
]
}
I want to group them again by merging the color_prop and shp_prop to an array inside the object like below:
{
"Administration": [
{
"shp_name": "Village Boundary",
"color_prop": ["#000000","#FFFFFF"],
"shp_prop": ["Batu Ampar","Sungai Jawi"]
},
],
"Land_use": [
{
"shp_name": "Land Use 2019",
"color_prop": ["#000000"],
"shp_prop": ["Grassland"]
},
]
}
I really appreciate it if someone could help me to solve this problem. Thank you.
You could introduce a helper that converts "rows" into "columns".
function toColumns(rows) {
const columns = {};
const keys = Object.keys(rows[0]);
for (const key of keys) columns[key] = [];
for (const row of rows) {
for (const key of keys) {
columns[key].push(row[key]);
}
}
return columns;
}
This helper converts a structure like:
[
{ a: 1, b: 2 },
{ a: 3, b: 4 },
]
Into:
{ a: [1, 3], b: [2, 4] }
With this helper defined you can convert your groups into the desired structure in the following manner:
for (const category in items) {
const columns = toColumns(items[category]);
// use the shp_name of the first item instead of an array
columns.shp_name = columns.shp_name[0];
items[category] = columns;
}
function toColumns(rows) {
const columns = {};
const keys = Object.keys(rows[0]);
for (const key of keys) columns[key] = [];
for (const row of rows) {
for (const key of keys) {
columns[key].push(row[key]);
}
}
return columns;
}
const items = {
"Administration": [
{
"shp_name": "Village Boundary",
"color_prop": "#000000",
"shp_prop": "Batu Ampar"
},
{
"shp_name": "Village Boundary",
"color_prop": "#FFFFFF",
"shp_prop": "Sungai Jawi"
}
],
"Land_use": [
{
"shp_name": "Land Use 2019",
"color_prop": "#000000",
"shp_prop": "Grassland"
},
]
}
for (const category in items) {
const columns = toColumns(items[category]);
columns.shp_name = columns.shp_name[0];
items[category] = columns;
}
console.log(items);
Replace items[category] = columns with result[category] = columns (where result is defined as const result = {} before the loop) if you don't want to mutate the original object.
Related
I tried looking into it but couldn't find the specific use case for my scenario. I am sure it is fairly simple but I am stuck on this for days. Any help will be appreciated
const stores = [
{
id: "61f27aeb766e4b2924532f98",
xName: 'SaurabhTest2',
merchantIDs: [ "61f27bca766e4b2924532fb3" ],
totalMerAcc: 1,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: [ "61f2788e766e4b2924532f54", "61f277b8766e4b2924532f31" ],
totalMerAcc: 2,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: [ "61f277b8766e4b2924532f31" ],
totalMerAcc: 1,
oneTimeData: 100
}]
Desired output:
[
{
id: "61f27aeb766e4b2924532f98",
xName: 'SaurabhTest2',
merchantIDs: [ "61f27bca766e4b2924532fb3" ],
totalMerAcc: 1,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: [ "61f2788e766e4b2924532f54", "61f277b8766e4b2924532f31" ],
totalMerAcc: 2,
oneTimeData: 100
}]
First the stores should merged based on "id" but then also check if the merchantsIDs already exists between the 2 mergers (there could be more than 2 same storeIDs here so more objects), and include all the distinct ones in the merchantIDs within an object and then total it in "totalMerAcc" as well to return something like above.
Here's the code I have written this this point:
function mergeRecurrentsStores(businessStores) {
const result = businessStores.map((item) => {
return [
item[0],
...item[1]
.reduce((accumulator, currentStore) => {
const key = currentStore.id.toString();
const innerItem =
accumulator.get(key) ||
Object.assign({}, currentStore, {
xName: currentStore.xName,
merchantIDs: currentStore.merchantIDs,
totalMerAcc: 0,
oneTimeData: currentStore.oneTimeData,
});
if(innerItem.merchantIDs.some(i => i.includes(currentStore.merchantIDs)) {
}
innerItem.totalMerAcc += currentStore.totalMerAcc;
return accumulator.set(key, innerItem);
}, new Map())
.values(),
];
});
return result;}
The example structure is inside item[1] which I am reducing. You can neglect the item[0] case. That's just for next thing in pipeline.
Any help is welcomed.
Array's map implements a mapping operation. Mapping operations produce a 1:1 result, one result item for each input item. But you don't want that, so map isn't the right tool.
If you were doing functional programming with predefined, reusable reducer functions, reduce might be the right tool, but if you aren't (and you don't seem to be, your reducer is inline), it's just an over-complicated loop. Instead, let's do a nice simple loop.
const byId = new Map();
for (const store of stores) {
let previous = byId.get(store.id);
if (!previous) {
// New one
byId.set(store.id, store);
} else {
// Merge with previous
// NOTE: If you don't want to modify the object in place, change
// `const previous` above to `let previous` and uncomment this:
/*
previous = {...previous, merchantIDs: [...previous.merchantIDs]};
byId.set(previous.id, previous);
*/
for (const merchantID of store.merchantIDs) {
if (!previous.merchantIDs.includes(merchantID)) {
previous.merchantIDs.push(merchantID);
}
}
previous.totalMerAcc = previous.merchantIDs.length; // It seems unnecessary to have `totalMerAcc`
}
}
const result = [...byId.values()];
Live Example:
const stores = [
{
id: "61f27aeb766e4b2924532f98",
xName: 'SaurabhTest2',
merchantIDs: ["61f27bca766e4b2924532fb3"],
totalMerAcc: 1,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: ["61f2788e766e4b2924532f54", "61f277b8766e4b2924532f31"],
totalMerAcc: 2,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: ["61f277b8766e4b2924532f31"],
totalMerAcc: 1,
oneTimeData: 100
}
];
const byId = new Map();
for (const store of stores) {
const previous = byId.get(store.id);
if (!previous) {
// New one
byId.set(store.id, store);
} else {
// Merge with previous
// NOTE: If you don't want to modify the object in place, change
// `const previous` above to `let previous` and uncomment this:
/*
previous = {...previous, merchantIDs: [...previous.merchantIDs]};
byId.set(previous.id, previous);
*/
for (const merchantID of store.merchantIDs) {
if (!previous.merchantIDs.includes(merchantID)) {
previous.merchantIDs.push(merchantID);
}
}
previous.totalMerAcc = previous.merchantIDs.length; // It seems unnecessary to have `totalMerAcc`
}
}
const result = [...byId.values()];
console.log(JSON.stringify(result, null, 4));
.as-console-wrapper {
max-height: 100% !important;
}
For completeness, though, we can do it with reduce (because reduce is a Swiss-army knife, you can do anything related to arrays with it, because again it's basically a loop):
const result = [...stores.reduce((byId, store) => {
const previous = byId.get(store.id);
if (!previous) {
return byId.set(store.id, store);
}
const merchantIDs = [...new Set([...previous.merchantIDs, ...store.merchantIDs])];
return byId.set(store.id, {
...previous,
merchantIDs,
totalMerAcc: merchantIDs.length,
});
}, new Map()).values()];
Live Example:
const stores = [
{
id: "61f27aeb766e4b2924532f98",
xName: 'SaurabhTest2',
merchantIDs: ["61f27bca766e4b2924532fb3"],
totalMerAcc: 1,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: ["61f2788e766e4b2924532f54", "61f277b8766e4b2924532f31"],
totalMerAcc: 2,
oneTimeData: 100
},
{
id: "61f2769b766e4b2924532f1f",
xName: 'SaurabhTest',
merchantIDs: ["61f277b8766e4b2924532f31"],
totalMerAcc: 1,
oneTimeData: 100
}
];
const result = [...stores.reduce((byId, store) => {
const previous = byId.get(store.id);
if (!previous) {
return byId.set(store.id, store);
}
const merchantIDs = [...new Set([...previous.merchantIDs, ...store.merchantIDs])];
return byId.set(store.id, {
...previous,
merchantIDs,
totalMerAcc: merchantIDs.length,
});
}, new Map()).values()];
console.log(JSON.stringify(result, null, 4));
.as-console-wrapper {
max-height: 100% !important;
}
In JavaScript I have 2 object arrays that have the same objects but are in a different order. I'm trying to figure out how to sort one array based on the order of the other. There is a unique field they both share (sortField below) I'm just failing on figuring out how to sort with it. Here's an example of my arrays:
sorter array:
[
{
"displayName": "Party",
"sortField": "com.uniqueXbd",
"elementId": "PtyListPanel"
}, {
"displayName": "Group",
"sortField": "com.uniqueARd",
"elementId": "GrpListPaneARd"
}, {
"displayName": "Leader",
"sortField": "com.uniqueEcF",
"elementId": "LeaderListPaneEcF"
}
]
needsSorted array:
[
{
"displayName": "Group",
"sortField": "com.uniqueARd",
"elementId": "GrpListPaneARd"
}, {
"displayName": "Leader",
"sortField": "com.uniqueEcF",
"elementId": "LeaderListPanel"
}, {
"displayName": "Party",
"sortField": "com.uniqueXbd",
"elementId": "PtyListPaneEcF"
}
]
I'm guessing it's going to look something like this?
needsSorted.sort((a, b) => {
if(sorter.sortField...){
return 1
})
Thanks
const output = [];
sortedArray.forEach( sortedItem => {
const matchingItem = unsortedArray.find( unsortedItem => unsortedItem.sortField === sortedItem.sortField );
if(matchingItem){
output.push(matchingItem);
}
});
Since you know the second array is the order you want the items from the first array to be in, you should loop through it. Then find the matching item from the first list, and push it into your output in that order.
You can make a sorting lookup that maps the sort key to the index in the original array. Then in your sort, you can look it up for both objects in the comparison.
This replaces the repeated need to lookup the index in the original array for each comparison with a constant time object lookup so it should be more performant for larger arrays at the expense of the space for the lookup object.
let sortObj = [{"displayName": "Party","sortField": "com.uniqueXbd","elementId": "PtyListPanel"}, {"displayName": "Group","sortField": "com.uniqueARd","elementId": "GrpListPaneARd"}, {"displayName": "Leader","sortField": "com.uniqueEcF","elementId": "LeaderListPaneEcF"}]
let needsSorted = [{"displayName": "Group","sortField": "com.uniqueARd","elementId": "GrpListPaneARd"}, {"displayName": "Leader","sortField": "com.uniqueEcF","elementId": "LeaderListPanel"}, {"displayName": "Party","sortField": "com.uniqueXbd","elementId": "PtyListPaneEcF"}]
let sortLookup = sortObj.reduce((obj, item, idx) => {
obj[item.sortField] = idx
return obj
}, {})
needsSorted.sort((a,b) => sortLookup[a.sortField] - sortLookup[b.sortField])
console.log(needsSorted)
var obj = [
{
"one": 1,
"two": 9
}, {
"one": 3,
"two": 5
}, {
"one": 1,
"two": 2
}
];
var obj = [
{
"one": 1,
"two": 2,
}, {
"one": 1,
"two": 9
}, {
"one": 3,
"two": 5
}
];
obj.sort(function(a, b) {
return a["one"] - b["one"] || a["two"] - b["two"];
});
const sortedIndexes = sorter.map(i => i.sortField);
needsSorted.sort((a, b) => {
const aIndex = sortedIndexes.findIndex((i) => i === a.sortField);
const bIndex = sortedIndexes.findIndex((i) => i === b.sortField);
return aIndex - bIndex;
})
Given that you just want to compare the two arrays and make sure they are still the same, I would go about it differently:
const first = sorted.sort((a, b) => a.localCompare(b))
const second = needsSorting.sort((a, b) => a.localCompare(b))
if (JSON.stringify(first) != JSON.stringify(second)) {
console.log("the array was modified!");
}
const sortOrder = sorted.map(item => item.sortField);
needsSorted.sort((a, b) => {
return sortOrder.indexOf(a.sortField) > sortOrder.indexOf(b.sortField) ? 1 : -1;
});
const fields = sorted.map(x => x.sortField);
const value = x => fields.indexOf(x.sortField);
needSorted.sort((a, b) => value(a) - value(b));
console.log(needSorted);
const sorted = [
{
displayName: "Party",
sortField: "com.uniqueXbd",
elementId: "PtyListPanel"
},
{
displayName: "Group",
sortField: "com.uniqueARd",
elementId: "GrpListPaneARd"
},
{
displayName: "Leader",
sortField: "com.uniqueEcF",
elementId: "LeaderListPaneEcF"
}
];
const needSorted = [
{
displayName: "Group",
sortField: "com.uniqueARd",
elementId: "GrpListPaneARd"
},
{
displayName: "Leader",
sortField: "com.uniqueEcF",
elementId: "LeaderListPanel"
},
{
displayName: "Party",
sortField: "com.uniqueXbd",
elementId: "PtyListPaneEcF"
}
];
const fields = sorted.map(x => x.sortField);
const value = x => fields.indexOf(x.sortField);
needSorted.sort((a, b) => value(a) - value(b));
console.log(needSorted);
I have a JSON-like hierarchy of JS objects in the following format:
[
{
subs: [ ...other objects... ]
},
...other objects...
]
I wrote a method that returns the number of levels of such a hierarchy:
/* Returns the depth of the tree. */
public getDepth(): number {
function f(obj: object): number {
let depth = 0;
if (obj['subs'].length > 0) {
obj['subs'].forEach((s: object) => {
const tempDepth = f(s);
if (tempDepth > depth) depth = tempDepth;
});
}
return depth + 1;
}
if (this.tree.length > 0)
return Math.max(...this.tree.map((s: object) => f(s)));
else return 0;
}
It works but it's too complicated. Then, I've found this, much cleaner code: https://stackoverflow.com/a/16075976/5214911
The only difference is that I have not one base object but an array of objects as root. How could I simplify the code to spare that extra if and iteration?
Example data:
const data1 = []; // depth: 0
const data2 = [{}, {}, {}]; // depth: 1
const data3 = [{}, // depth: 5
{
"subs": [{
"subs": [{
"subs": [{}]
}, {
"subs": [{
"subs": [{}]
}]
}]
}, {
"subs": [{
"subs": [{}]
}]
}]
},
{}
];
You could map the depth of every children and take the maximum value of it.
function getDepth(array) {
return 1 + Math.max(0, ...array.map(({ subs = [] }) => getDepth(subs)));
}
const
data1 = [],
data2 = [{}, {}, {}],
data3 = [{}, { subs: [{ subs: [{ subs: [{}] }, { subs: [{ subs: [{}] }] }] }, { subs: [{ subs: [{}] }] }] }, {}];
console.log(getDepth(data1) - 1); // 0
console.log(getDepth(data2) - 1); // 1
console.log(getDepth(data3) - 1); // 5
Use Array.prototype.map() to change each item of array to its length and then use Math.max() on array
getDepth = function (obj) {
var depth = 0;
if (obj.children) {
obj.children.forEach(function (d) {
var tmpDepth = getDepth(d)
if (tmpDepth > depth) {
depth = tmpDepth
}
})
}
return 1 + depth
}
let arr = [...];
let depths = arr.map(x => getDepth(x))
let maxDepth = Math.max(...depths)
I have an array that looks like this:
var array = [[
{ "loc": {} },
{ "distance": 6.4 },
{ "zip1": "06120" },
{ "zip2": "06095" },
{ "group": 1 },
{ "weight": 1119 }
], [
{ "loc": {} },
{ "distance": 6.41 },
{ "zip1": "06095" },
{ "zip2": "06120" },
{ "group": 2 },
{ "weight": 41976 }
], [
{ "loc": {} },
{ "distance": 6.41 },
{ "zip1": "06095" },
{ "zip2": "06120" },
{ "group": 1 },
{ "weight": 41976 }
]];
Now I want to take the array values based on the property values for show in HTML.
Expected output is split into array with "group" property. I also need to store in HTML with based on group, as shown in the example below:
group 1:
all zip1's under group 1
group 2:
all zip1's under group 2
I tried using a loop but I didn't manage to get the right answer:
for (var k = 0; k < array.length; k++) {
var array1 = array[k];
if (flag[array1[2]["zip1"]]) continue;
flag[array1[2]["zip1"]] = true;
output2.push(array1);
}
So help me to find split the array show in HTML with group wise
Using reduce, you can create an object with each group value as key and an array of zip1 as values like this:
Then loop through the Object.entries, to create the HTML:
const array = [[{"loc":{}},{"distance":6.4},{"zip1":"06120"},{"zip2":"06095"},{"group":1},{"weight":1119}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":2},{"weight":41976}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":1},{"weight":41976}]];
const merged = array.reduce((r, a) =>{
const { group } = a.find(n => n.group)
const { zip1 } = a.find(n => n.zip1)
r[group] = r[group] || []
r[group].push(zip1)
return r;
},{})
const output = document.getElementById('output');
Object.entries(merged).forEach(([group, zips]) => {
const h1 = document.createElement('h1');
h1.innerHTML = "group " + group
const span = document.createElement('span');
span.innerHTML = `Zip1 - ${zips} (in group - ${group})`;
output.appendChild(h1)
output.appendChild(span)
})
<div id="output"></div>
I have an array like bellow each index contains different set of objects,I want to create an uniformal data where object missing in each index will with Value:0 ,
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
how can I get an array like bellow using above above array
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
{axis:"Sending Money",value:0,id:6},
{axis:"Other",value:0,id:7},
],
[
{axis:"Email",value:0,id:1},
{axis:"Social Networks",value:0,id:2},
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
There are two functions:
getAllEntries that find all objects and stores them into a variable accEntries. Then accEntries is used to search for all occurrences in a sub-array of d. This whole process is done in checkArray.
checkArray is used to fetch all found and not-found entries in d. Both Arrays (found and not-found) are then used to build a new sub-array that contains either found entries with certain values and/or not-found entries with values of 0.
Hope this helps:
var d = [
[
{
axis: 'Email',
value: 59,
id: 1
},
{
axis: 'Social Networks',
value: 56,
id: 2
},
],
[
{
axis: 'Sending Money',
value: 18,
id: 6
},
{
axis: 'Other',
value: 15,
id: 7
},
]
];
function getAllEntries(array) {
var uniqueEntries = [];
array.forEach(function (subarray) {
subarray.forEach(function (obj) {
if (uniqueEntries.indexOf(obj) === - 1) uniqueEntries.push(obj);
});
});
return uniqueEntries;
}
function checkArray(array, acceptedEntries) {
var result = [];
array.forEach(function (subArray) {
var subResult = [];
var foundEntries = [];
subArray.forEach(function (obj) {
if (foundEntries.indexOf(obj.axis) === - 1) foundEntries.push(obj.axis);
});
var notFound = acceptedEntries.filter(function (accepted) {
return foundEntries.indexOf(accepted.axis) === - 1;
});
foundEntries.forEach(function (found) {
subArray.forEach(function (obj) {
if (obj.axis === found) subResult.push(obj);
});
});
notFound.forEach(function (notfound, index) {
subResult.push({
axis: notfound.axis,
value: 0,
id: notfound.id
});
});
result.push(subResult);
});
return result;
}
var accEntries = getAllEntries(d);
var result = checkArray(d, accEntries);
console.log(result);
You can loop over the array to find all the unique objects and then again loop over to push the values that are not present comparing with the array of objects of unique keys.
You can use ES6 syntax to find if an object with an attribute is present like uniKeys.findIndex(obj => obj.axis === val.axis); and the to push with a zero value use the spread syntax like d[index].push({...val, value: 0});
Below is the snippet for the implementation
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
{axis:"Social Networks",value:89,id:2},
]
];
var uniKeys = [];
$.each(d, function(index, item) {
$.each(item, function(idx, val){
const pos = uniKeys.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
uniKeys.push(val);
}
})
})
$.each(d, function(index, item) {
var temp = [];
$.each(uniKeys, function(idx, val){
const pos = item.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
d[index].push({...val, value: 0});
}
})
})
console.log(d);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
How about a short shallowCopy function (Object.assign is not available in IE) and otherwise less than 10 new lines of code?
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2}
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7}
]
];
var newD_0 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
var newD_1 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
newD_0[2].id = 0;
newD_0[3].id = 0;
newD_1[0].id = 0;
newD_1[1].id = 0;
d = [newD_0, newD_1];
function shallowCopy(obj) {
var copy = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
console.log(JSON.stringify(d));
RESULT:
[
[
{
"axis":"Email",
"value":59,
"id":1
},
{
"axis":"Social Networks",
"value":56,
"id":2
},
{
"axis":"Sending Money",
"value":18,
"id":0
},
{
"axis":"Other",
"value":15,
"id":0
}
],
[
{
"axis":"Email",
"value":59,
"id":0
},
{
"axis":"Social Networks",
"value":56,
"id":0
},
{
"axis":"Sending Money",
"value":18,
"id":6
},
{
"axis":"Other",
"value":15,
"id":7
}
]
]