Best way to reorganize array of objects - javascript

I need reorganize and array of linked objects by id to only one tree object. The depth level is unknown, so that I think that it should be done recursively. What is the most efficient way?
I have the next array of objects:
const arrObj = [
{
"id": 1,
"children": [
{
"id": 2
},
{
"id": 3
}
]
},
{
"id": 2,
"children": [
{
"id": 4
},
{
"id": 5
}
]
},
{
"id": 3,
"children": [
{
"id": 6
}
]
},
{
"id": 4
}
]
I want restructure for have a only one object like a tree:
const treeObj = {
"id": 1,
"children": [
{
"id": 2,
"children": [
{
"id": 4
},
{
"id": 5
}
]
},
{
"id": 3,
"children": [
{
"id": 6
}
]
}
]
}
Each object has other many properties.

You can use a recursive mapping function over all the children.
const arrObj = [ { "id": 1, "children": [ { "id": 2 }, { "id": 3 } ] }, { "id": 2, "children": [ { "id": 4 }, { "id": 5 } ] }, { "id": 3, "children": [ { "id": 6 } ] }, { "id": 4 } ];
const res = arrObj[0];//assuming the first element is the root
res.children = res.children.map(function getChildren(obj){
const child = arrObj.find(x => x.id === obj.id);
if(child?.children) child.children = child.children.map(getChildren);
return child || obj;
});
console.log(res);

Related

JavaScript: get all possible values of a field in a two-dimensional array

From API I get such a json:
{
"purposes": [
{
"id": 1,
"code": "for-calls"
},
{
"id": 2,
"code": "task-management"
},
{
"id": 3,
"code": "messenger"
}
],
"availabilities": [
{
"id": 1,
"code": "free"
},
{
"id": 2,
"code": "free-basic-plan"
},
{
"id": 3,
"code": "trial-version"
}
],
"ecosystems": [
{
"id": 1,
"code": "browse-widget"
},
{
"id": 2,
"code": "web-app"
},
{
"id": 3,
"code": "installation-on-your-server"
}
]
}
How do I iterate over this json to get a new array containing the field values 'code' from each element. As a result, there should be /catalog/filter/value_of_code in each element of the array.
const obj = {
"purposes": [
{
"id": 1,
"code": "for-calls"
},
{
"id": 2,
"code": "task-management"
},
{
"id": 3,
"code": "messenger"
}
],
"availabilities": [
{
"id": 1,
"code": "free"
},
{
"id": 2,
"code": "free-basic-plan"
},
{
"id": 3,
"code": "trial-version"
}
],
"ecosystems": [
{
"id": 1,
"code": "browse-widget"
},
{
"id": 2,
"code": "web-app"
},
{
"id": 3,
"code": "installation-on-your-server"
}
]
}
const arr = Object.values(obj).reduce((acc, e) => [...acc, ...e.map(o => `/catalog/filter/${o.code}`)] ,[]);
console.log( arr );
Lets say you are receiving this JSON object in serverResp object.
Now lets check if any catalogs are present in the object.
if(Object.keys(serverResp).length)
Next we shall iterate over the catalogs
Object.keys(serverResp).forEach((cat) => )
Each cat represents a catalog. Next we shall fetch array against each key inside our forloop
const catItems = serverResp[cat];
catItems will contain all the items of a particular catalog. Now lets iterate over each catItems to get the "code" value
catItems.forEach(item => console.log(item.code));
Final piece of code will look like below-
const outputArray = [];
if(Object.keys(serverResp).length) {
Object.keys(serverResp).forEach((cat)=> {
const catItems = serverResp[cat];
catItems.forEach(item =>
outputArray.push(`/catalog/filter${item.code}`));
});
}
console.log(outputArray);

How to replace an array of objects with an array including newer objects in TypeScript?

For example: I have an array of objects like this:
let arrayOfObjects: [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
Now I want to replace or overwrite the array above with the same array, but including different values (same keys, different values):
let arrayOfObjects: [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
];
How can this be done in TypeScript?
Try this JS
let oneArray = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
let twoArray = [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
];
let newArray = Object.assign([], oneArray, twoArray);
console.log(newArray);
In TS
interface Data {
oneArray: array;
twoArray: array;
}
function merge(data: Data) {
return Object.assign([], data.oneArray, data.twoArray);
}
let user = {
oneArray: [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
],
twoArray: [
{ "id": 0, "name": "Not Available" },
{ "id": 1, "name": "Not Ready" },
{ "id": 2, "name": "Not Started" }
]
};
console.log(merge(user));
Sure :)
let arrayOfObjects = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
const newValues = ['Not Available', 'Not Ready', 'Not Started'];
newValues.forEach((value, i) => {
arrayOfObjects.find(o => o.id === i).name = value;
});
console.log(arrayOfObjects);
I wouldn't recommend this though: functional programming is awesome.
let arrayOfObjects = [
{ "id": 0, "name": "Available" },
{ "id": 1, "name": "Ready" },
{ "id": 2, "name": "Started" }
];
let names = ['Not Available', 'Not Ready', 'Not Started']
let result = arrayOfObjects.map((user, index) => ({ ...user, name: names[index] }))
console.log(result)

Filtering an array based on multiple arrays inputs

I have three arrays.
1. Existing viewers array - existingViewers
New viewers array - newViewers
Permitted Viewers array - permittedViewers
permittedViewers is used for rendering the drop-down. And I wish to filter the newViewers and existingViewers entries from the permittedViewers.
I am doing this as three steps. And I am afraid this is not the optimized way. Can someone suggest the ideal way of doing this?
The expected result is
[
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let grouped = [...existingViewers, ...newViewers]
let viewerFilter = grouped.map(viewer => { return viewer.viewerId; });
let filteredPermittedViewers = permittedViewers.filter(viewer => !viewerFilter.includes(viewer.id));
console.log(filteredPermittedViewers)
I'd make a Set of the ids of the first two arrays, and then filter the third by whether the set includes the id. (Sets have O(1) lookup time)
let existingViewers=[{"viewerId":1,"name":"name1"},{"viewerId":2,"name":"name2"}],newViewers=[{"viewerId":3,"name":"name3"}],permittedViewers=[{"id":1,"name":"name1"},{"id":2,"name":"name2"},{"id":3,"name":"name3"},{"id":4,"name":"name4"},{"id":5,"name":"name5"},{"id":6,"name":"name6"}];
const ids = new Set([...existingViewers, ...newViewers].map(({ viewerId }) => viewerId));
const output = permittedViewers.filter(({ id }) => !ids.has(id));
console.log(output);
You can compress all three statements into a single statement -- just replace the variable name with the statement that creates it:
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let filteredPermittedViewers = permittedViewers.filter(viewer => ! [...existingViewers, ...newViewers].map(viewer => viewer.viewerId).includes(viewer.id));
console.log(filteredPermittedViewers)

Need to match object values from one array to the object values from another array

In the following code. I am trying to get the id of a variant that matches the selected objects
const selected = [ { "id": 14 }, { "id": 95 } ]
const variants = [
{
"id": 1,
"option_values": [ { "id": 7 }, { "id": 95, } ]
},
{
"id": 2,
"option_values": [ { "id": 8 }, { "id": 95, } ]
},
{
"id": 3,
"option_values": [ { "id": 14 }, { "id": 95, } ]
}
]
function filterVaiant() {
return variants.filter( options => {
// return id 3 because it matches the selected objects
});
}
console.log(filterVaiant());
The filter function should return variant id:3 because it has the same option values as the selected option values.
follow up
const colors = require('colors');
const selected = [ { "id": 14 }, { "id": 95 }, { "id": 21 } ]
let selected_ids = selected.map(e=>e.id);
const variants = [
{
"id": 1,
"option_values": [ { "id": 7 }, { "id": 95, } ]
},
{
"id": 2,
"option_values": [ { "id": 8 }, { "id": 95, } ]
},
{
"id": 3,
"option_values": [ { "id": 14 }, { "id": 95, } ]
},
{
"id": 4,
"option_values": [ { "id": 14 }, { "id": 95, }, { "id": 21 } ]
}
]
let vID = variants.filter(e=> e.option_values.every(e=> selected_ids.indexOf(e.id) > -1));
console.log("answer:",vID) // Returns 3 and 4 but should only return 4
in this scenario vID is 3 and 4 but should only return 4 because it is the only one that matches the selections exactly.
You can use .filter and .every to achieve this like
const selected = [ { "id": 14 }, { "id": 95 } ];
var ids = selected.map(e=>e.id);
const variants = [
{
"id": 1,
"option_values": [ { "id": 7 }, { "id": 95, } ]
},
{
"id": 2,
"option_values": [ { "id": 8 }, { "id": 95, } ]
},
{
"id": 3,
"option_values": [ { "id": 14 }, { "id": 95, } ]
}
];
var o = variants.filter(e=> e.option_values.every(e=> ids.indexOf(e.id) > -1));
console.log(o)
What's happening here is, get all the selected ids in an array, then filter the variants array based on if every entry of option_values has all the entries as the selected ids.
For each object, check that all items in selected appear in option_values (ops alias) by checking that the length is equal, and using Array.every(), and Array.find():
const selected = [ { "id": 14 }, { "id": 95 } ]
const variants = [{"id":1,"option_values":[{"id":7},{"id":95}]},{"id":2,"option_values":[{"id":8},{"id":95}]},{"id":3,"option_values":[{"id":14},{"id":95}]}];
function filterVaiant() {
return variants.filter(({ option_values: ops }) => {
if (selected.length !== ops.length) return false;
return selected.every(({ id }) =>
ops.find((o) => id === o.id));
});
}
console.log(filterVaiant());
Using a Set to store the selected Id's to look up and Array#every in a filter
const selected = [ { "id": 14 }, { "id": 95 } ]
const variants = [
{
"id": 1,
"option_values": [ { "id": 7 }, { "id": 95, } ]
},
{
"id": 2,
"option_values": [ { "id": 8 }, { "id": 95, } ]
},
{
"id": 3,
"option_values": [ { "id": 14 }, { "id": 95, } ]
}
]
function filterVaiant(selected) {
let ids = new Set(selected.map(({id})=>id))
return variants.filter( o => o.option_values.every(({id})=>ids.has(id)));
}
console.log(filterVaiant(selected));
Another solution with map only indexof and join:
const selected = [ { "id": 14 }, { "id": 95 } ]
const variants = [
{
"id": 1,
"option_values": [ { "id": 7 }, { "id": 95, } ]
},
{
"id": 2,
"option_values": [ { "id": 8 }, { "id": 95, } ]
},
{
"id": 3,
"option_values": [ { "id": 14 }, { "id": 95, } ]
}
]
function filterVaiant(selected) {
return variants[variants.map(obj => obj.option_values).map(outterarray => outterarray.map(innerArray=>innerArray.id).join('-') ).indexOf(selected.map(eb=>eb.id).join('-'))];
}
console.log(filterVaiant(selected));

Building new JSON from existing one

I want to build an new JSON from existing one. The source has sections and rubrics that I no longer need for a listing. The new object called 'items' should have an array of the items.
The final JSON should be sorted by attribute 'name' and look like
{
"items": [
{
"id": 10000006,
"name": "Boah"
},
{
"id": 10000013,
"name": "Gut"
},
{
"id": 10000003,
"name": "Ipsum"
},
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000002,
"name": "Stet"
}
]
}
For building the new JSON I get this source:
{
"sections": [
{
"name": "FooBar",
"rubrics": [
{
"name": "Foo",
"items": [
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000002,
"name": "Stet"
},
{
"id": 10000003,
"name": "Ipsum"
}
]
},
{
"name": "Bar",
"items": [
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000006,
"name": "Boah"
}
]
}
]
},
{
"name": "BlahBloob",
"rubrics": [
{
"name": "Bla",
"items": [
{
"id": 10000013,
"name": "Gut"
}
]
},
{
"name": "Bloob",
"items": [
{
"id": 10000014,
"name": "Name"
},
{
"id": 10000015,
"name": "Lorem"
}
]
}
]
}
]
}
What do you think? How can I do this with plain JavaScript or maybe TypeScript?
Thanks for reading and have time for my question. And thanks for reply in advance.
Here you go. You just need to iterate over each rubric of each section of your source to get the items. At the end, sort your list of items by items, and you're done.
This example uses ES6 syntax, but it's easy to convert it to ES5 if needed.
function extractItems(source) {
const items = [];
for (const section of source.sections) {
for (const rubric of section.rubrics) {
items.push(...rubric.items);
}
}
items.sort((a, b) => a.name.localeCompare(b.name));
return { items };
}
A more functional approach use map and reduce to pick the rubrics and merge them.
data.sections
.map(section => section.rubrics) // get rubrics
.reduce((a, b) => a.concat(b)) // merge rubrics
.map(rubric => rubric.items) // get items from each rubric
.reduce((a, b) => a.concat(b)) // merge items
.sort((a, b) => a.name.localeCompare(b.name)); // sort
function(oldObj) {
var newObj = {
"items": []
};
oldObj.sections.forEach(function(section) {
section.rubrics.forEach(function(rubric) {
rubric.items.forEach(function(item) {
newObj.items.push(item);
});
});
});
newObj.items = newObj.items.sort(function(a, b) {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return newObj;
}
And simply use JSON.parse() and JSON.stringify() to convert JSON to and from objects.
It might help you
var data ={
"sections": [
{
"name": "FooBar",
"rubrics": [{"name": "Foo", "items": [{"id": 10000001,"name": "Lorem"}, {"id": 10000002,"name": "Stet"}, {"id": 10000003,"name": "Ipsum"}]
}, {
"name": "Bar",
"items": [{
"id": 10000004,
"name": "Name"
}, {
"id": 10000005,
"name": "Lorum"
}, {
"id": 10000006,
"name": "Boah"
}]
}]
}, {
"name": "BlahBloob",
"rubrics": [{
"name": "Bla",
"items": [{
"id": 10000013,
"name": "Gut"
}]
}, {
"name": "Bloob",
"items": [{
"id": 10000014,
"name": "Name"
}, {
"id": 10000015,
"name": "Lorem"
}]
}]
}]
};
var itemObj = {};
var itemArr = [];
var sections = data.sections;
for(var i=0;i<sections.length;i++)
{
for(var j=0;j<sections[i].rubrics.length;j++){
for(var k=0;k<sections[i].rubrics[j].items.length;k++){
var itemObj;
itemObj['id'] = sections[i].rubrics[j].items[k].id;
itemObj['name'] = sections[i].rubrics[j].items[k].name;
itemArr.push(itemObj);
}
}
}
var finalObj = {"items":itemArr};
console.log(finalObj);
JSFiddle

Categories

Resources