How to group related fields in JSONL? - javascript

I am using Shopify's bulk operation mutation in GraphQL to fetch bulk data for products, invoices, transactions etc.
Shopify returns this data as JSONL. Each line is an object and if it is related to another item then it will have a __parentid which can be used to figure out its parent.
For example, the returned data will look something like this:
const data = [
{
id: "1",
__typename: "parent"
},
{
id: "2",
__parentId: "1",
__typename: "child"
},
{
id: "3",
__parentId: "2",
__typename: "grandChild"
},
{
id: "4",
__parentId: "2",
__typename: "grandChild"
}
]
In order to save this in my database, I need to nest each child value within its parent.
So the above would become
[
{
id: "1",
__typename: "parent",
child: [
{
id: "2",
__parentId: "1",
__typename: "child",
grandChild: [
{
id: "3",
__parentId: "2",
__typename: "grandChild"
},
{
id: "4",
__parentId: "2",
__typename: "grandChild"
}
]
}
]
},
]
This is one approach I tried where you can see I am trying to attach the children to the parent object and then once it has been moved, delete the original object.
export const reduceBulkResult = (chunks) => {
chunks.reverse()
chunks.forEach(chunk => {
const parent = chunks.find(node =>{
return node.id === chunk.__parentId
})
if( parent ) {
if( parent[chunk.__typename] ) {
parent[chunk.__typename].push(chunk)
} else {
parent[chunk.__typename] = [chunk]
}
}
})
return chunks;
}
console.log("reduceBulkResult", reduceBulkResult(data))
[
{
"__parentId": "2",
"__typename": "grandChild",
"id": "4"
},
{
"__parentId": "2",
"__typename": "grandChild",
"id": "3"
},
{
"__parentId": "1",
"__typename": "child",
"grandChild": [
{
"__parentId": "2",
"__typename": "grandChild",
"id": "4"
},
{
"__parentId": "2",
"__typename": "grandChild",
"id": "3"
}
],
"id": "2"
},
{
"__typename": "parent",
"child": [
{
"__parentId": "1",
"__typename": "child",
"grandChild": [
{
"__parentId": "2",
"__typename": "grandChild",
"id": "4"
},
{
"__parentId": "2",
"__typename": "grandChild",
"id": "3"
}
],
"id": "2"
}
],
"id": "1"
}
]
As you can see, there is a lof of duplication here and I need to get rid of the original objects some how and just keep the parent objects which have all the nested data.
I feel like I'm missing a trick here. There must be an easier way.
Shopify have some good tips such asReading the JSONL file in reverse makes it easier to group child nodes

I get the parent with filter() and then use recursive in getChildren to get the result.
I cannot find a simple way and you may change codes to make it more simple.
const data = [ { id: "1", __typename: "parent", }, { id: "2", __parentId: "1", __typename: "child", }, { id: "3", __parentId: "2", __typename: "grandChild", }, { id: "4", __parentId: "2", __typename: "grandChild", }, ]; const o = [ { id: "1", __typename: "parent", child: [ { id: "2", __parentId: "1", __typename: "child", grandChild: [ { id: "3", __parentId: "2", __typename: "grandChild", }, { id: "4", __parentId: "2", __typename: "grandChild", }, ], }, ], }, ];
const convert = data => {
const parent = data.filter(el => !el.__parentId);
const getChildren = (data, parentId) => {
let store = {};
for (const obj of data) {
if (obj.__parentId === parentId) {
const children = data.reduce((a, b) => {
if (b.__parentId === obj.id) {
if (a[b.__typename]) {
a[b.__typename].push({ ...b, ...getChildren(data, b.id) });
} else {
a[b.__typename] = [{ ...b, ...getChildren(data, b.id) }];
}
}
return a;
}, {});
store = { ...store, ...obj, ...children };
}
}
return store;
};
return parent.map(el => {
const children = data.reduce((a, b) => {
if (b.__parentId === el.id) {
if (a[b.__typename]) {
a[b.__typename].push({ ...b, ...getChildren(data, el.id) });
} else {
a[b.__typename] = [{ ...b, ...getChildren(data, el.id) }];
}
}
return a;
}, {});
return { ...el, ...children };
});
};
const output = convert(data);
console.log(output);

Related

Update only after all the checks are passed

I want to update array of object only after all the checks are passed. I have array of object representing all the articles and other array of object all the available stock.
I want to check the if all the articles are in stock then only I want to update the stock array. Here is my code:
const articles = [{
"id": "1",
"quantity": "4"
}, {
"id": "2",
"quantity": "8"
}, {
"id": "4",
"quantity": "1"
}];
let stock = [
{
"id": "1",
"stock": "6"
},
{
"id": "2",
"stock": "6"
},
{
"id": "3",
"stock": "2"
},
{
"id": "4",
"stock": "2"
}
];
articles.map(article => {
stock.map(item => {
if(item.id === article.id){
if(article.quantity <= item.stock){
item.stock = item.stock - article.quantity;
} else {
console.log('error');
throw error;
}
}
});
});
Problem with this solution is that it updates stock for id = 1 even though id = 2 is not enough. I am trying to check if all the articles are in stock are in enough quantity and update(minus) them in stock array. So in this case code will update stock array like this:
stock = [
{
"id": "1",
"stock": "2"
},
{
"id": "2",
"stock": "6"
},
{
"id": "3",
"stock": "2"
},
{
"id": "4",
"stock": "2"
}
];
Can someone suggest how can I fix this?
You can use Array.prototype.find(). Note: The snippet assumes that all articles have an "id" property, it doesn't check for "art_id".
const articles = [{
"id": "1",
"quantity": "4"
}, {
"id": "2",
"quantity": "8"
}, {
"id": "4",
"quantity": "1"
}];
let stock = [{
"id": "1",
"stock": "6"
},
{
"id": "2",
"stock": "6"
},
{
"id": "3",
"stock": "2"
},
{
"id": "4",
"stock": "2"
}
];
// Use Array.prototype.some() to check for items that don't have enough stock
const haveEnough = !articles.some(article =>
// Turns "1" into 1
parseInt(
// Find the stock with the same id as the article
stock.find(stock => stock.id === article.id).stock
) <
parseInt(article.quantity))
console.log(haveEnough)
You can then update the stock in
if (haveEnough) {
// Do something
}
I am assuming that the art_id key in the articles array should be id.
Unfortunately you can't do it all in one loop and you will instead have to loop through all the articles first to find out if their stock is enough and then loop though them again to change the existing stock:
const articles = [
{
id: '1',
quantity: '4',
},
{
id: '2',
quantity: '5',
},
{
id: '4',
quantity: '1',
},
];
let stock = [
{
id: '1',
stock: '6',
},
{
id: '2',
stock: '6',
},
{
id: '3',
stock: '2',
},
{
id: '4',
stock: '2',
},
];
// Are there any articles that do not have enough stock
const outOfStockArticles = articles.find((article) => {
const articleStock = stock.find((stock) => stock.id === article.id);
return Number.parseInt(article.quantity) > Number.parseInt(articleStock.stock);
});
// If there are no out of stock articles - change existing stock values
if (!outOfStockArticles) {
articles.forEach((article) => {
const articleStock = stock.find((stock) => stock.id === article.id);
articleStock.stock = Number.parseInt(articleStock.stock) - Number.parseInt(article.quantity);
});
}
There is an inconsistency in your articles array, I suppose that every articles has an "art_id", and not some an "id" and some an "art_id".
You can simply find your article using the Array.prototype.find function, and then by the isAvailable function check that an article is present in the right quantity in the stock list.
Now you can use the Array.prototype.every function that do exactly what you want! It will return true only if every element of the array satisfy the condition, in our case the isAvailable function.
Here a simple fiddle:
const articles = [{
"art_id": "1",
"quantity": "4"
}, {
"art_id": "2",
"quantity": "8"
}, {
"art_id": "4",
"quantity": "1"
}];
const stock = [{
"id": "1",
"stock": "6"
},
{
"id": "2",
"stock": "6"
},
{
"id": "3",
"stock": "2"
},
{
"id": "4",
"stock": "2"
}
];
const isAvaiable = (article) => {
return stock.find(element => element.id === article.art_id).stock >= article.quantity;
}
if (articles.every(isAvaiable)) {
console.log("I can update")
} else {
console.log("I cannot update")
}

es6 create three dimensional array [duplicate]

This question already has answers here:
Build tree array from flat array in javascript
(34 answers)
Closed 2 years ago.
I have incoming data in this format:
const worldMap = [
{
"name": "Germany",
"parentId": null,
"type": "Country",
"value": "country:unique:key:1234",
"id": "1",
},
{
"name": "North Rhine",
"parentId": "1",
"type": "State",
"value": "state:unique:key:1234",
"id": "2",
},
{
"name": "Berlin",
"parentId": "1",
"type": "State",
"value": "state:unique:key:1234",
"id": "3",
},
{
"name": "Dusseldorf",
"parentId": "2",
"type": "city",
"value": "city:unique:key:1234",
"id": "4",
},
{
"name": "India",
"parentId": null,
"type": "Country",
"value": "country:unique:key:1234",
"id": "5",
},
];
I want the output to be something like this:
[
{
label: "Germany",
value: "country:unique:key:1234",
subs: [
{
label: "North Rhine",
value: "state:unique:key:1234",
subs: [
{
label: "Dusseldorf",
value: "city:unique:key:1234",
}
]
},
{
label: "Berlin",
value: "state:unique:key:1234",
}
]
}
,
{
"label": "India",
"value": "country:unique:key:1234"
}
]
Basically, it is a three dimensional array with first level being the Countrie, second States and third Cities. I have tried the following code:
let tempCountries = [];
worldMap.map((world) => {
if (world.parentId == null && world.type == "Country") {
tempCountries.push({label: world.name, value: world.value, id: world.id});
}
});
console.log("=== countries ===", tempCountries);
tempCountries.map((tempCountry) => {
const states = worldMap.find((x) => x.parentId == tempCountry.id);
console.log("=== states ===", states);
if (states !== undefined) {
}
});
In the first loop I got all the values for the countries. Next I am trying to append states and cities to the original countries, I got from the first loop. But I am not able to do so. The code should be verbose with minimum number of loops. Could anyone please help achieve this ?
Thanks
You could take a single loop and store all relations between child/parents and parents/childs.
const
data = [{ name: "Germany", parentId: null, type: "Country", value: "country:unique:key:1234", id: "1" }, { name: "North Rhine", parentId: "1", type: "State", value: "state:unique:key:1234", id: "2" }, { name: "Berlin", parentId: "1", type: "State", value: "state:unique:key:1234", id: "3" }, { name: "Dusseldorf", parentId: "2", type: "city", value: "city:unique:key:1234", id: "4" }, { name: "India", parentId: null, type: "Country", value: "country:unique:key:1234", id: "5" }],
tree = ((data, root) => {
var t = {};
data.forEach(({ id, parentId, name: label, value }) => {
Object.assign(t[id] = t[id] || {}, { label, value });
t[parentId] = t[parentId] || {};
(t[parentId].subs = t[parentId].subs || []).push(t[id]);
});
return t[root].subs;
})(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to order an array of objects in array of categories in javascript?

I have the following array of objects
{
"nome": "jose",
"categoria": [
{ "id": "1" },
{ "id": "3" },
]
},
{
"nome": "maria",
"categoria": [
{ "id": "2" },
]
},
{
"nome": "pedro",
"categoria": [
{ "id": "1" },
]
}
I have to reorder in another array of categories. Something like this:
{
"id": "1",
"pessoas": [
{
"nome": "jose",
"categoria": [
{ "id": "1" },
{ "id": "3" },
]
},
{
"nome": "pedro",
"categoria": [
{ "id": "1" },
]
},
]
},
{
"id": "2",
"pessoas": [
{
"nome": "maria",
"categoria": [
{ "id": "2" }
]
},
]
},
I have try with the function reduce(), but I couldn't because it is not an object, but a array of objects (categoria)
const group = data.reduce((r, array) => {
r[array.categoria.id] = [...r[array.categoria.id] || [], array];
return r;
}, {});
Someone can help me please?
You could take an object for grouping by id. Inside of reduce, categoria is iterated as well for getting the needed id.
var data = [{ nome: "jose", categoria: [{ id: "1" }, { id: "3" }] }, { nome: "maria", categoria: [{ id: "2" }] }, { nome: "pedro", categoria: [{ id: "1" }] }],
result = Object.values(data.reduce((r, o) => {
o.categoria.forEach(({ id }) => {
if (!r[id]) r[id] = { id, pessoas: [] };
r[id].pessoas.push(o);
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Creating a new array from the parents of array child element

I have the following array
{
"id": "111",
"name": "1111",
"children": [
{
"id": "22222",
"name": "2222",
"children": [
{
"id": "AAAA",
"name": "AAAA",
"children": [
{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [
{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [
{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}
]
}
And I would like to create an array with the parents of a child by its ID.
So for example if I wanted to get the path to the child with ID "FFF", then the array would look like something like this:
["1111", "2222", "BBB", "FFF"]
How could I go about doing that?
You could take an iterative and recursive approach.
function getItems({ children, ...object }, key, value) {
var temp;
if (object[key] === value) return [object];
if (children) children.some(o => temp = getItems(o, key, value));
return temp && [object, ...temp];
}
var data = { id: "111", name: "1111", children: [{ id: "22222", name: "2222", children: [{ id: "AAAA", name: "AAAA", children: [{ id: "DDD", name: "DDD" }, { id: "EEE", name: "EEE" }] }, { id: "BBBB", name: "BBB", children: [{ id: "FFF", name: "FFF" }, { id: "GGG", name: "GGG", children: [{ id: "7777", name: "7777" }, { id: "8888", name: "8888" }] }] }] }] };
console.log(getItems(data, 'id', 'FFF'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could implement a recursive search to find all paths and return the correct one when you reach the desired name-value pair.
const isObject = (obj) => obj === Object(obj);
let data = loadData();
let expected = [ '1111', '2222', 'BBB', 'FFF' ];
let actual = findPath(data, 'name', 'FFF');
console.log(JSON.stringify(expected) === JSON.stringify(actual));
function findPath(data, key, value, includeIndicies=false) {
let opts = { found : null, includeIndicies : includeIndicies };
findPathInternal(data, key, value, opts, []);
return opts.found;
}
function findPathInternal(node, key, val, opts, path) {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
findPathInternal(node[i], key, val, opts, opts.includeIndicies ? path.concat(i) : path);
}
} else if (isObject(node)) {
if (node[key] === val) {
opts.found = path.concat(val); return; // Exit
} else {
let keys = Object.keys(node);
for (let i = 0; i < keys.length; i++) {
findPathInternal(node[keys[i]], key, val, opts, path.concat(node[key]));
}
}
}
};
function loadData() {
return {
"id": "111",
"name": "1111",
"children": [{
"id": "22222",
"name": "2222",
"children": [{
"id": "AAAA",
"name": "AAAA",
"children": [{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

push elements of each object inside each object inside another array

I have two arrays, one is my original one called data which consists of :
const datas = [
{
name: 'core Test',
item: [
{
name: 'test/core/core.js',
item: "item1"
}
]
},
{
name: 'users Test',
item: [
{
name: 'test/users/user.js',
item: "item2"
}
]
}
]
And i have another array called replace, which i'm trying to push each of its elements inside my original one, inside the
const replace = [
{
type: "test1",
number: "1",
},
{
type: "test2",
number: "2",
}
]
Here is my code :
const transformedData = datas.map(data => {
data.item = data.item.map(x => ({
name: x.name,
type: replace.map(y=>{return y;})
}))
return data
})
The output i get :
[
{
"name": "core Test",
"item": [
{
"name": "test/core/core.js",
"type": [
{ "type": "test1", "number": "1" },
{ "type": "test2", "number": "2" }
]
}
]
},
{
"name": "users Test",
"item": [
{
"name": "test/users/user.js",
"type": [
{ "type": "test1", "number": "1" },
{ "type": "test2", "number": "2" }
]
}
]
}
]
The output i want :
[
{
"name": "core Test",
"item": [
{
"name": "test/core/core.js",
"type": { "type": "test1", "number": "1" }
}
]
},
{
"name": "users Test",
"item": [
{
"name": "test/users/user.js",
"type": { "type": "test2", "number": "2" }
}
]
}
]
This is because you're mapping all the way through the replace array every single time for each time you're inside of a value inside of datas. Instead you want to keep track of the index with your original map so then you only have one instance each time.
Try something like:
const transformedData = datas.map((data, index) => {
data.item = data.item.map(x => ({
name: x.name,
type: replace[index]
}))
return data;
});

Categories

Resources