Adding parent ID information to properties in a hierarchy - javascript

I need to grab all of the ID values from the previous object and build out an array of the IDs in each parent item. This needs to be done in a generic manner and cannot use the name of the property. However, each property does inherit a base class. There may be other arrays on the structure that do not inherit from the SubResource class, so only those that do, should be added to the identifierHierarchy.
export abstract class SubResource {
public id: number;
public identifierHierarchy: number[] = [];
}
Given the following snippet of data
let data = [{
"id": "1",
"name": "Deer, spotted",
"parents": [
{
"id": "133",
"name": "Jaime Coldrick",
"children": [
{
"id": "0723",
"name": "Ardys Kurten",
"grandchildren": [
{
"id": "384",
"name": "Madelle Bauman"
},
{
"id": "0576",
"name": "Pincas Maas"
},
{
"id": "5",
"name": "Corrie Beacock"
}
]
}]
}]
}]
I need for the values on the objects to now be.
[{
"id": "1",
"name": "Deer, spotted",
"parents": [{
"id": "133",
"name": "Jaime Coldrick",
"identifierHierarchy": ["1"],
"children": [{
"id": "0723",
"name": "Ardys Kurten",
"identifierHierarchy": ["1", "133"],
"grandchildren": [{
"id": "384",
"name": "Madelle Bauman",
"identifierHierarchy": ["1", "133", "0723"]
},
{
"id": "0576",
"name": "Pincas Maas",
"identifierHierarchy": ["1", "133", "0723"]
},
{
"id": "5",
"name": "Corrie Beacock",
"identifierHierarchy": ["1", "133", "0723"]
}
]
}]
}]
}]

I think this could work:
function addHierarchy(items: any[], chain: string[], inherited: string[] = []) {
items.forEach(item => {
if (inherited.length)
item.identifierHierarchy = inherited;
if (chain.length)
addHierarchy(item[chain[0]], chain.slice(1), [...inherited, item.id]);
});
}
addHierarchy(data, ['parents', 'children', 'grandchildren']);

Not as type-safe as I would like, but...
export class RestHierarchyService {
public static assignHierarchyIdentifiers(subResource: SubResource | any, parentIdentifiers: any): void {
const composedSubResources = Object.entries(subResource)
.filter(([key, value]) => key !== 'identifierHierarchy' && value.constructor === Array)
.map(([key, value]) => Object.values(value).reduce(x => x));
subResource.identifierHierarchy = parentIdentifiers;
composedSubResources.forEach(x => RestHierarchyService.assignHierarchyIdentifiers(x, [...parentIdentifiers, subResource.id]));
}
}
ancestry.forEach(x => x.parents.forEach(y => RestHierarchyService.assignHierarchyIdentifiers(y, [x.id])));

Related

How can I inner join with two object arrays in JavaScript?

I need inner join with two array in javascript like this:
array1 =
[
{
"id": 1,
"name": "Tufan"
},
{
"id": 2,
"name": "Batuhan"
},
{
"id": 3,
"name": "Hasan"
}
]
array2 =
[
{
"name": "yyy",
"externalid": "1",
"value": "Asd"
},
{
"name": "aaaa"
"externalid": "2",
"value": "ttt"
}
]
expectedArray =
[
{
"id": 1,
"name": "Tufan",
"externalid": "1",
"value": "Asd"
},
{
"id": 2,
"name": "Batuhan",
"externalid": "2",
"value": "ttt"
}
]
rules:
on: array2.externalid = array1.id
select: array1.id, array1.name, array2.externalid, array2.value
My approach:
array1.filter(e => array2.some(f => f.externalid == e.id));
// I need help for continue
How can I make this?
Doesn't matter information: I use ES5 and pure javascript
You can do it like this:
const res = array2.map((item) => {
const related = array1.find((el) => el.id == item.externalid);
return { ...item, ...related };
});
Using a map to loop over the array2 and a find to get the array1 relative.

Most performant way to sort a deeply nested array of objects by another deeply nested array of objects

As an example - I've included a one element array that contains an object that has a Children key, which is an array of objects and each object also has its' own Children key that contains another array.
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
{
"Id": "100",
"DisplayName": "1-2",
},
]
}
]
}
]
There is a second array of objects that I would like to compare the first array of objects to, with the intention of making sure that the first array is in the same order as the second array of objects, and if it is not - then sort until it is.
Here is the second array:
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "100",
"DisplayName": "1-2",
},
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
]
}
]
}
]
The data that this will run on can be up in the tens of thousands - so performance is paramount.
What I'm currently attempting is using a utility method to convert each element of the second array into a keyed object of objects e.g.
{
1: {
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}
]
}
}
This allows fast look up from the top level. I'm wondering if I should continue doing this all the way down or if there is an idiomatic way to accomplish this. I considered recursion as well.
The order of the already sorted array is not based on Id - it is arbitrary. So the order needs to be preserved regardless.
Assuming same depth and all Id's exist in each level of each object use a recursive function that matches using Array#findIndex() in sort callback
function sortChildren(main, other) {
other.forEach((o, i) => {
if (o.children) {
const mChilds = main[i].children, oChilds = o.children;
oChilds.sort((a, b) => {
return mChilds.findIndex(main => main.Id === a.Id) - mChilds.findIndex(main => main.Id === b.Id)
});
// call function again on this level passing appropriate children arrays in
sortChildren(mChilds, oChilds)
}
})
}
sortChildren(data, newData);
console.log(JSON.stringify(newData, null, ' '))
<script>
var data = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "3",
"DisplayName": "1-2",
},
{
"Id": "4",
"DisplayName": "3-4",
},
]
}]
}]
var newData = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}]
}]
</script>

Javascript filter for multidimensional json object

Can't use javascript filter in multi-dimensional object.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
);
}
var filtered = object.filter(searchFor);
console.log(filtered);
This is how I am trying, inside the searchFor card.name I am getting the correct card name but filtering is returning all the cards.Its not filtering.
Could any help me with this.
An empty array isn't considered falsey in Javascript. So instead of returning the result of filtering the cards array, test its length.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
function searchFor(item) {
return item.cards.filter(
(card) => {
return card.name.indexOf("Raman") !== -1;
}
).length != 0;
}
var filtered = object.filter(searchFor);
console.log(filtered);
You were returning the filtered array, which would produce a TRUE result whenever cards existed. So you can just turn that into a boolean, by saying when the item.cards.filter(...).length > 0.
var object = [{
"id": "1",
"name": "General",
"cards": [{
"id": "1",
"name": "shawn"
}, {
"id": "2",
"name": "neo"
}]
}, {
"id": "2",
"name": "CEO",
"cards": [{
"id": "1",
"name": "Raman"
}, {
"id": "2",
"name": "Sheena"
}]
}]
var searchFor = (card) => card.name.indexOf("Raman") > -1;
var filteredCards = object.reduce((cards, item) => cards.concat(item.cards.filter(searchFor)), []);
var filteredObj = object.map(i => {
i.cards = i.cards.filter(searchFor);
return i;
}).filter(i => i.cards.length)
console.log(filteredCards, filteredObj)
Updated
I updated the code snippet to produce either the cards which were found. I also provide a method for returning all objects which contain the needed cards, and filter out the other cards.
// HTML Part
<div class="filter-list">
<button class="filter" data-filter-key="all">all</button>
<button class="filter" data-filter-key="open">open</button>
<button class="filter" data-filter-key="done">done</button>
</div>
// CSS Part
.filter:hover,
.filter:focus,
[data-active-filter="all"] .filter[data-filter-key="all"],
[data-active-filter="done"] .filter[data-filter-key="done"],
[data-active-filter="open"] .filter[data-filter-key="open"] {
text-decoration: underline;
}
[data-active-filter="open"] [data-completed="true"],
[data-active-filter="done"] [data-completed="false"] {
display: none;
}
// Script Part
(function () {
const mainNode = document.querySelector("main");
const filters = document.querySelector(".filter-list");
for (const filter of filters.children) {
filter.addEventListener("click", () => {
mainNode.setAttribute(
"data-active-filter",
filter.getAttribute("data-filter-key")
);
});
}
mainNode.setAttribute("data-active-filter", "all");
})();

map 2 array of object using findIndex es6

I have 2 array of object, one is the preloaded list, one is the selected items. My problem is couldn't make the selected items checked on checkboxes.
https://jsfiddle.net/8usvfzv9
class HelloWidget extends React.Component {
constructor(props) {
super(props);
this.list = [{
"id": "exhibitions",
"name": "Exhibitions"
}, {
"id": "festivals_n_concerts",
"name": "Festivals & Concerts"
}, {
"id": "grand_opening",
"name": "Grand Opening"
}, {
"id": "meeting",
"name": "Meeting"
}, {
"id": "party",
"name": "Party"
}, {
"id": "product_launches",
"name": "Product Luanches"
}, {
"id": "roadshows",
"name": "Roadshows"
}, {
"id": "sporting_events",
"name": "Sporting Events"
}, {
"id": "trade_show",
"name": "Trade Show"
}]
this.selectedList = [{
"id": "grand_opening",
"name": "Grand Opening",
"space_event_id": "grand_opening"
}, {
"id": "trade_show",
"name": "Trade Show",
"space_event_id": "trade_show"
}]
}
render() {
return (<div>
{this.list.map(obj => <div><br /><input
key={obj.name}
checked={this.selectedList.findIndex(o => o.id === obj.id)}
type="checkbox" >{obj.name}</input></div>)}
</div>
)
}
}
I think this line is wrong
checked={this.selectedList.findIndex(o => o.id === obj.id)}
base on the output result. Any clue how to use findIndex?
As 'checked' prop only works with boolean and findIndex returns number, you can modify as below:
checked={this.selectedList.findIndex(o => o.id === obj.id) !== -1}

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