Objects with Array values into a tree - javascript

I have an array of objects where every object has an array of values. The data structure is not ideal but it's the only way I can access it. I'm trying to turn this structure into a tree structure so I can build a D3 indented collapsible table.
I've tried to modify some previous answers I've found but have yet to be successful. Here is a link to the current JSFiddle I've been working on.
http://jsfiddle.net/COLTstreet/fsve7w2L/25/
This is a small example of how the data comes to me.
{
"data": [
{
"items": [
"All Other",
"4C FOODS CORP"
],
"hints": {
"index": 0
}
},
{
"items": [
"All Other",
"PBNA"
],
"hints": {
"index": 14
}
},
{
"items": [
"All Other",
"PRIVATE LABEL"
],
"hints": {
"index": 15
}
},
{
"items": [
"Base Water",
"CCNA"
],
"hints": {
"index": 18
}
},
{
"items": [
"Base Water",
"CRYSTAL GEYSER"
],
"hints": {
"index": 19
}
}
]
}
I need the code to finish like this:
[
{
"Category": "All Other",
"children": [
{
"name": "4C FOODS CORP"
},
{
"name": "PBNA"
},
{
"name": "PRIVATE LABEL"
}
]
},
{
"Category": "Base Water",
"children": [
{
"name": "CCNA"
},
{
"name": "CRYSTAL GEYSER"
}
]
}
]

One solution is to first use Array.reduce() in conjunction with destructuring to generate an object for group the elements by the category, and in a second step use Array.map() over the generated object entries to get the desired output:
const input = {
"data": [
{"items": ["All Other", "4C FOODS CORP"], "hints": {"index": 0}},
{"items": ["All Other", "PBNA"], "hints": {"index": 14}},
{"items": ["All Other", "PRIVATE LABEL"], "hints": {"index": 15}},
{"items": ["Base Water", "CCNA"], "hints": {"index": 18}},
{"items": ["Base Water", "CRYSTAL GEYSER"], "hints": {"index": 19}}
]
};
// Group by the category.
let res = input.data.reduce((acc, {items: [cat, val]}) =>
{
acc[cat] = acc[cat] || [];
acc[cat].push({name: val});
return acc;
}, {});
// Generate desired output structure.
res = Object.entries(res).map(([k, v]) => ({category: k, children: v}));
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Related

Transforming a complex object into an array

I am creating a React app using data from an API. the response data is a long complicated object that looks something like this:
{
"Frozen Products": [
{
"_id": "6181849285e8d8f86be2d9df",
"name": "Peas (800g)",
"category_id": "6181841060c425f76e57b603",
"slug": "vegetables",
"quantity": 16,
"price": {
"amount": 3.00
},
"thumbnail":"URI ...",,
"images": [
"URI ...",
],
"synonyms": [
"Veggies"
],
},
//etc for many key/values inside this object ..
}
I need to access the information do display a category ("Frozen Products" in this case) and all the product names and images under it.
But I think I need to access each Key/Value pair by loopin through their index (example data[0]) not the Key name since they keys are dynamically created by an API. So how can I change this to:
{
[ "Frozen Products": [
{
"_id": "6181849285e8d8f86be2d9df",
"name": "Peas (800g)",
"category_id": "6181841060c425f76e57b603",
"slug": "vegetables",
"quantity": 16,
"price": {
"amount": 3.00
},
"thumbnail":"URI ...",,
"images": [
"URI ...",
],
"synonyms": [
"Veggies"
],
},
]
I think you need something like this:
let data = {
"Frozen Products": [
{
"_id": "6181849285e8d8f86be2d9df",
...
}
],
"Another Category": [...]
}
let categories = Object.keys(data).map((key) => ({categoryName: key, products: data[key]}));
you can loop through an object with for in iterator.
No matter what key names are
for(key in object) {
let value = object[key];
}

Convert flat data to 3 level hierarchy/tree

I'm currently converting my flat data to a hierarchy with 2 levels, but I now need to add an additional 3rd level to this data. I haven't been able to figure out how to modify by existing method so far. I'm open to completely new methods also.
Here is the code I'm currently using to do the conversion:
chartdata = sfdata.data.reduce((acc, {
items: [cat, val, salesTY, salesLY, unitsTY, unitsLY]
}) => {
acc[cat] = acc[cat] || [];
acc[cat].push({
name: val,
salesTY: salesTY,
salesLY: salesLY,
unitsTY: unitsTY,
unitsLY: unitsLY
});
return acc;
}, {});
// Generate desired output structure.
chartdata = Object.entries(chartdata).map(([k, v]) => ({
category: k,
children: v
}));
It maps to the different categories and then converts that map to a category>children structure
And here is the fiddle where I'm using this tree structure: http://jsfiddle.net/zt4nhxcw/3/
I've started a new fiddle here with the new Brand data included: http://jsfiddle.net/t1uz85b2/
The goal is to add a 3rd level. So every child at the 2nd level would have Brand underneath of them.
Here is a very small snippet of how the data comes in:
[
{
"items": [
"SSD",
"PBNA",
"MOUNTAIN DEW",
851255.3500000001,
672407.8399999997,
782364.9999999991,
641579.0000000006
],
"hints": {
"index": 0
}
},
{
"items": [
"Energy",
"RED BULL NORTH AMERICA",
"RED BULL",
836632.2299999997,
654021.2899999995,
267216,
214321.00000000015
],
"hints": {
"index": 1
}
},
{
"items": [
"SSD",
"PBNA",
"PEPSI",
478704.02999999974,
392746.69999999995,
533557.0000000006,
457008.0000000001
],
"hints": {
"index": 4
}
},
{
"items": [
"Energy",
"RED BULL NORTH AMERICA",
"RED BULL EDITIONS",
449618.55000000016,
328150.8999999997,
162428.9999999999,
117521.00000000001
],
"hints": {
"index": 5
}
},
{
"items": [
"SSD",
"CCNA",
"COKE",
349685.7899999996,
276766.95,
445485.0000000002,
351214.0000000003
],
"hints": {
"index": 9
}
}
]
And here is the final structure I'm trying to achieve:
[
{
"category": "SSD",
"children": [
{
"brand": "PBNA",
"children": [
{
"name": "MOUNTAIN DEW",
"salesTY": 851255.3500000001,
"salesLY": 672407.8399999997,
"unitsTY": 782364.9999999991,
"unitsLY": 641579.0000000006
}
]
},
{
"brand": "CCNA",
"children": [
{
"name": "COKE",
"salesTY": 349685.7899999996,
"salesLY": 276766.95,
"unitsTY": 445485.0000000002,
"unitsLY": 351214.0000000003
}
]
}
]
},
{
"category": "Energy",
"children": [
{
"brand": "RED BULL NORTH AMERICA",
"children": [
{
"name": "RED BULL",
"salesTY": 836632.2299999997,
"salesLY": 654021.2899999995,
"unitsTY": 267216,
"unitsLY": 214321.00000000015
},
{
"name": "RED BULL EDITIONS",
"salesTY": 449618.55000000016,
"salesLY": 328150.8999999997,
"unitsTY": 162428.9999999999,
"unitsLY": 117521.00000000001
}
]
}
]
}
]
You could take a dynamic approach and take an array with the keys for all values and a limit of the wanted depth.
const
data = [{ items: ["SSD", "PBNA", "MOUNTAIN DEW", 851255.3500000001, 672407.8399999997, 782364.9999999991, 641579.0000000006], hints: { index: 0 } }, { items: ["Energy", "RED BULL NORTH AMERICA", "RED BULL", 836632.2299999997, 654021.2899999995, 267216, 214321.00000000015], hints: { index: 1 } }, { items: ["SSD", "PBNA", "PEPSI", 478704.02999999974, 392746.69999999995, 533557.0000000006, 457008.0000000001], hints: { index: 4 } }, { items: ["Energy", "RED BULL NORTH AMERICA", "RED BULL EDITIONS", 449618.55000000016, 328150.8999999997, 162428.9999999999, 117521.00000000001], hints: { index: 5 } }, { items: ["SSD", "CCNA", "COKE", 349685.7899999996, 276766.95, 445485.0000000002, 351214.0000000003], hints: { index: 9 } }],
keys = ['category', 'brand', 'name', 'salesTY', 'salesLY', 'unitsTY', 'unitsLY'],
limit = 2,
result = data
.reduce((temp, { items }) => {
keys
.slice(0, limit)
.reduce(function (r, k, i) {
if (!r[items[i]]) {
r[items[i]] = { _: [] };
r._.push({ [k]: items[i], children: r[items[i]]._ });
}
return r[items[i]];
}, temp)
._
.push(keys
.slice(limit)
.reduce((o, k, i) => (o[k] = items[i + limit], o), {})
);
return temp;
}, { _: [] })
._;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Is there a way to update multiple documents with different values in mongoose?

I'm feeling like there probably isn't a way to do this, but here's what I want to accomplish, I have an array of products to update like so:
const productsToUpdate = [
{
"imgUrls": {
"base": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
],
"side": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
]
},
"_id": "5dd6173cf50c1d1a40fe7c2c",
"category": "MENS",
"name": "JavaScript is Cool",
"price": "27.50"
},
{
"imgUrls": {
"base": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
],
"side": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
]
},
"_id": "5dd6173cf50c1d1a40fe7c2c",
"category": "MENS",
"name": "Testing",
"price": "25.50"
}
]
This data structure matches the data of products currently stored in my database. Basically, I need to find a current product by _id and be able to update either the category, price, imgUrls, or name or all of the above.
I've done some research and know I can grab all the ids from the toUpdate arr and then do something like
Collection.update({ _id: { $in: arrOfIds }, {}, {multi: true}))
But I'm not sure how to fill in the query to have the correct category/price/name etc.
I would greatly appreciate any guidance or if you need more information please let me know!
The "mass-update" or rather the most efficient way of that kind of updates is called bulkWrite. Since there are is more fields in your database and you don't want to lose them you need to use $set operator.
const productsToUpdate = [
{
"imgUrls": {
"base": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
],
"side": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
]
},
"_id": "5dd6173cf50c1d1a40fe7c2c",
"category": "MENS",
"name": "JavaScript is Cool",
"price": "27.50"
},
{
"imgUrls": {
"base": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
],
"side": [
"https://files.cdn.printful.com/files/ba1/ba13faa1332b7f18ec847cb9f4d79868_preview.png"
]
},
"_id": "5dd6173cf50c1d1a40fe7c2c",
"category": "MENS",
"name": "Testing",
"price": "25.50"
}
];
let toUupdate = product => ({
updateOne: {
"filter": { "_id": product._id },
"update": { "$set": { category: product.category, name: product.name, price: product.price } }
}
})
db.collection.bulkWrite(productsToUpdate.map(toUupdate);
console.log(updates);

How to create a new json row by combining elements from multiple other json rows dynamically

I have json data in fields that are split across rows. I want to have all these fields in a single row. I would like to write a javascript function that takes the original input and produces the output. I am very new to javascript. I would like to do this in a dynamic way so that I don't have to explicitly name the new fields -- rather iterate through all the fields in each row one by one and append them to a new row, with the same names/values. Thanks in advance.
Starting json:
[
{
"name": "Sara Smith",
"dob": "19831002"
},
{
"title": "director",
"emails": [
"ssmith#gmail.com",
"sarasmith#yahoo.com"
]
},
{
"phones": [
{
"type": "home",
"number": "3452345432"
},
{
"type": "work",
"number": "3452345343"
}
]
}
]
Desired end state json:
[
{
"name": "Sara Smith",
"dob": "19831002"
"title": "director",
"emails": [
"ssmith#gmail.com",
"sarasmith#yahoo.com"
]
"phones": [
{
"type": "home",
"number": "3452345432"
},
{
"type": "work",
"number": "3452345343"
}
]
}
]
Use reduce:
const startJSON = [{
"name": "Sara Smith",
"dob": "19831002"
},
{
"title": "director",
"emails": [
"ssmith#gmail.com",
"sarasmith#yahoo.com"
]
},
{
"phones": [{
"type": "home",
"number": "3452345432"
},
{
"type": "work",
"number": "3452345343"
}
]
}
];
const endJSON = [startJSON.reduce((acc, curr) => ({ ...acc, ...curr }))];
console.log(endJSON);
.as-console-wrapper { max-height: 100% !important; top: auto; }
One possible solution is to use Array.reduce() in combination with Object.assign():
const input = [
{"name": "Sara Smith", "dob": "19831002"},
{
"title": "director",
"emails": ["ssmith#gmail.com", "sarasmith#yahoo.com"]
},
{
"phones": [
{"type": "home", "number": "3452345432"},
{"type": "work", "number": "3452345343"}
]
}
];
let output = input.reduce((acc, curr) =>
{
Object.assign(acc[0], curr);
return acc;
}, [{}]);
console.log(output);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Converting javascript object to ordered comma separated value

I am trying to get the items in the json arranged in an orderly manner. I was able to select the "term" values present in the json, but is it possible to arrange this in the manner I have shown in the expected output part? I have added a jsfiddle link to show where I have reached:
[
{
"Link": "http://testLink.com/1",
"_index": "test",
"_source": {
"Author": "SAM",
"Map": [
{
"Company": [
{
"Apple_Inc": [
{
"count": 1,
"term": "Apple"
}
],
"sector": "Technology",
"term": "Apple Inc",
"ticker": "AAPL",
"type": "BCap"
}
],
"count": 1,
"term": "Company"
},
{
"Country": [
{
"Canada": [
{
"Canada": [
{
"count": 1,
"term": "Toronto"
}
],
"count": 1,
"term": "Canada"
}
],
"United_States": [
{
"count": 1,
"term": "United States"
}
],
"currency": "Dollar (USD)",
"index": "DOW JONES INDUS. AVG , S&P 500 INDEX , NASDAQ COMPOSITE INDEX",
"region": "North Americas",
"term": "Canada"
}
],
"count": 1,
"term": "Country"
},
{
"Personality": [
{
"count": 1,
"term": "Bart Prince"
},
{
"count": 1,
"term": "Thomas"
},
{
"count": 1,
"term": "Deborah Hornstra"
},
{
"count": 1,
"term": "Henderson Sotheby"
},
{
"count": 1,
"term": "Max Alliance"
}
],
"count": 5,
"term": "Personality"
}
]
},
"id": "YMFT112"
},
{
"Link": "http://testLink.com/2",
"_id": "YMFT113",
"_index": "test",
"_source": {
"Author": "MAX",
"Map": [
{
"Company": [
{
"Microsoft Corp": [
{
"count": 1,
"term": "Microsoft"
}
],
"sector": "Technology",
"term": "Microsoft",
"ticker": "AAPL",
"type": "BCap"
}
],
"count": 1,
"term": "Company"
},
{
"Country": [
{
"Brazil": [
{
"count": 1,
"term": "Brazil"
}
],
"currency": "Dollar (USD)",
"region": "South Americas",
"term": "Brazil"
}
],
"count": 1,
"term": "Country"
},
{
"SalesRelated": [
{
"count": 1,
"term": "traffic"
}
]
},
{
"Personality": [
{
"count": 1,
"term": "Maximor"
},
{
"count": 1,
"term": "R.V.P"
},
{
"count": 1,
"term": "Wenger"
},
{
"count": 1,
"term": "SAF"
}
],
"count": 4,
"term": "Personality"
}
]
}
}
]
http://jsbin.com/exuwet/3/edit
Prompt Input
If field Selected = Country,
Expected Output:
YMFT112; Country; United States; United States; NA; http://testLink.com/1;
YMFT112; Country; Canada; Canada; Toronto; http://testLink.com/1;
YMFT113; Country; Brazil; Brazil; NA; http://testLink.com/2;
If field Selected = Company,
Expected Output:
YMFT112; Company; Apple Inc; Apple; http://testLink.com/1;
YMFT113; Company; Microsoft Corp; Microsoft; http://testLink.com/2;
You can use the JSON object when natively available or use JSON2 as a shim.
After that it's just a matter of using JavaScript's built in sorting capability. You supply a function that compares to array items against each other
var myArray = JSON.parse(jsonString);
myArray.sort(function(a, b){
var nameA = a._source.Map.Company.term;
var nameB = b._source.Map.Company.term;
if (nameA === nameB) {
return 0;
} else if (nameA < nameB) {
return -1
}
return 1;
});
With eval('(' + json_object + ')'), you will be able to create a JavaScript Object. This object will be an array, and you can acess the properties using ..
For example, if your json_object is called data, for example:
Then
var temp = eval('(' + data + ')'); // temp now is an array.
if you want to access the first _index or id from the json object:
"_index": "test",
"id": "YMFT112",
do alert(temp[0]._index), and it will show you "test". For the other properties, follow the same logic. This stackoverflow question, or the JSON page will help you understand what you have to do in other to have your task accomplished. Yahoo has an API called YUI which may be even more helpful.
Here is a solution using object-scan
// const objectScan = require('object-scan');
const data = [{"_index":"test","id":"YMFT112","_source":{"Author":"SAM","Map":[{"count":1,"term":"Company","Company":[{"sector":"Technology","ticker":"AAPL","Apple_Inc":[{"count":1,"term":"Apple"}],"term":"Apple Inc","type":"BCap"}]},{"count":1,"term":"Country","Country":[{"region":"North Americas","index":"DOW JONES INDUS. AVG , S&P 500 INDEX , NASDAQ COMPOSITE INDEX","United_States":[{"count":1,"term":"United States"}],"term":"Canada","currency":"Dollar (USD)","Canada":[{"count":1,"term":"Canada","Canada":[{"count":1,"term":"Toronto"}]}]}]},{"count":5,"term":"Personality","Personality":[{"count":1,"term":"Bart Prince"},{"count":1,"term":"Thomas"},{"count":1,"term":"Deborah Hornstra"},{"count":1,"term":"Henderson Sotheby"},{"count":1,"term":"Max Alliance"}]}]},"Link":"http://testLink.com/1"},{"_index":"test","_id":"YMFT113","_source":{"Author":"MAX","Map":[{"count":1,"term":"Company","Company":[{"sector":"Technology","ticker":"AAPL","Microsoft Corp":[{"count":1,"term":"Microsoft"}],"term":"Microsoft","type":"BCap"}]},{"count":1,"term":"Country","Country":[{"region":"South Americas","Brazil":[{"count":1,"term":"Brazil"}],"term":"Brazil","currency":"Dollar (USD)"}]},{"SalesRelated":[{"count":1,"term":"traffic"}]},{"count":4,"term":"Personality","Personality":[{"count":1,"term":"Maximor"},{"count":1,"term":"R.V.P"},{"count":1,"term":"Wenger"},{"count":1,"term":"SAF"}]}]},"Link":"http://testLink.com/2"}];
const find = (term, input) => {
const r = objectScan([`[*]._source.Map[*].${term}[*].**.term`], {
reverse: false,
filterFn: ({ key, parents, context }) => {
if (Object.values(parents[0]).some((e) => e instanceof Object)) {
return;
}
const root = parents[parents.length - 2];
context.push([
root.id || root._id,
parents[parents.length - 5].term,
key[key.length - 3].replace(/_/g, ' '),
...parents.slice(0, -7).filter((e) => !Array.isArray(e)).map((p) => p.term).reverse(),
root.Link
]);
}
})(input, []);
const maxLength = Math.max(...r.map((e) => e.length));
r
.filter((e) => e.length < maxLength)
.forEach((e) => e.splice(-1, 0, 'NA'.repeat(maxLength - e.length)));
return r;
};
console.log(find('Country', data).map((e) => e.join('; ')).join('\n'));
/* =>
YMFT112; Country; United States; United States; NA; http://testLink.com/1
YMFT112; Country; Canada; Canada; Toronto; http://testLink.com/1
YMFT113; Country; Brazil; Brazil; NA; http://testLink.com/2
*/
console.log(find('Company', data).map((e) => e.join('; ')).join('\n'));
/* =>
YMFT112; Company; Apple Inc; Apple; http://testLink.com/1
YMFT113; Company; Microsoft Corp; Microsoft; http://testLink.com/2
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Categories