Related
I have an dataset like this:
const array = [
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sedan',
carname: 'Audi a8',
type: 'car'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'cruiser',
carname: 'Harley',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sports',
carname: 'Ninja',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sedan',
carname: 'BMW',
type: 'car'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sports',
carname: 'Hayabusa',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'hatchback',
carname: 'Micra',
type: 'car'
},
]
NOTE: This is raw data with shorter length. Actual data will be like 10M+ enteries.
What I want is like I want to preserve the only 1 or 2 data from the array and rest bike data will be filter out from an Array.
Now for this, the method I choose is like this:
let newArray = []
let cou = 0
array.map((x) => {
if (x.type === 'bike') {
cou++;
// Want to preserve only 1 data of bike
if (cou < 2) {
newArray.push(x)
}
} else {
newArray.push(x)
}
})
Is there any best approach for handling this type of problem. Actually I want to reduce the array iteration as it consume the memory and time of the code.
Any solution or suggestion is highly appreciated. Thanks in advance for the interaction with the problem set.
You can use the filter method:
var bikeCount = 0;
var res = array.filter(x => {
if (x.type === "bike") {
bikeCount++;
return bikeCount < 2;
}
return true;
});
In this way you don't have to create a new array and push every single element into it.
I've used an online benchmark tool and it seems 35-40% faster than your solution.
You can solve that using Array.prototype.slice method:
const array = [
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sedan',
carname: 'Audi a8',
type: 'car'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'cruiser',
carname: 'Harley',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sports',
carname: 'Ninja',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sedan',
carname: 'BMW',
type: 'car'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'sports',
carname: 'Hayabusa',
type: 'bike'
},
{
id: '5c1b4ffc18e2d84b7d6feaf3',
model: 'hatchback',
carname: 'Micra',
type: 'car'
},
]
/*
Array.prototype.slice slices an arrray and returns sliced content
1st param : slice start index
2nd param : slice end index
*/
const filteredArray = array.slice(0, 2);
console.log(filteredArray);
Given a flat level array of objects, what's the most efficient and modern way to nest them based on a parent and id property? The top level objects have no parentId, and there's no limit to nest levels.
[{
id: 'OS:MacOS',
type: 'OS',
value: 'MacOS'
}, {
parentId: 'OS:MacOS',
id: 'Version:Catalina',
type: 'Version',
value: 'Catalina'
}, {
parentId: 'Version:Catalina',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}, {
id: 'OS:Windows',
type: 'OS',
value: 'Windows'
}, {
parentId: 'OS:Windows',
id: 'Version:7',
type: 'Version',
value: '7'
}, {
parentId: 'OS:MacOS',
id: 'Version:Mojave',
type: 'Version',
value: 'Mojave'
}, {
parentId: 'Version:Mojave',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}, {
parentId: 'OS:Windows',
id: 'Version:XP',
type: 'Version',
value: 'XP'
}, {
parentId: 'Version:XP',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}]
Where parentId matches up to a corresponding id field. Ideally transforming them to include a children array field along the lines of:
[{
id: 'OS:MacOS',
type: 'OS',
value: 'MacOS',
children: [
{
parentId: 'OS:MacOS',
id: 'Version:Catalina',
type: 'Version',
value: 'Catalina',
children: [
{
parentId: 'Version:Catalina',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}
]
},
{
parentId: 'OS:MacOS',
id: 'Version:Mojave',
type: 'Version',
value: 'Mojave',
children: [
{
parentId: 'Version:Mojave',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}
]
}
]
}, {
id: 'OS:Windows',
type: 'OS',
value: 'Windows',
children: [
{
parentId: 'OS:Windows',
id: 'Version:7',
type: 'Version',
value: '7'
},
{
parentId: 'OS:Windows',
id: 'Version:XP',
type: 'Version',
value: 'XP',
children: [
{
parentId: 'Version:XP',
id: 'Browser:Chrome',
type: 'Browser',
value: 'Chrome'
}
]
}
]
}]
Thoughts appreciated!
You could use reduce in recursive function that will pass down the current element id and compare it with parent id in nested calls.
const data = [{"id":"OS:MacOS","type":"OS","value":"MacOS"},{"parentId":"OS:MacOS","id":"Version:Catalina","type":"Version","value":"Catalina"},{"parentId":"Version:Catalina","id":"Browser:Chrome","type":"Browser","value":"Chrome"},{"id":"OS:Windows","type":"OS","value":"Windows"},{"parentId":"OS:Windows","id":"Version:7","type":"Version","value":"7"},{"parentId":"OS:MacOS","id":"Version:Mojave","type":"Version","value":"Mojave"},{"parentId":"Version:Mojave","id":"Browser:Chrome","type":"Browser","value":"Chrome"},{"parentId":"OS:Windows","id":"Version:XP","type":"Version","value":"XP"},{"parentId":"Version:XP","id":"Browser:Chrome","type":"Browser","value":"Chrome"}]
function nested(data, pid = undefined) {
return data.reduce((r, e) => {
if (e.parentId == pid) {
const obj = { ...e }
const children = nested(data, e.id);
if (children.length) obj.children = children;
r.push(obj)
}
return r;
}, [])
}
const result = nested(data);
console.log(result)
The reducer approach by Nenad works, but is pretty inefficient as it iterates through the data list n^2 times. Here is an O(n) solution:
function buildTree(data) {
const store = new Map(); // stores data indexed by it's id
const rels = new Map(); // stores array of children associated with id
const roots = []; // stores root nodes
data.forEach(d => {
store.set(d.id, d);
!rels.get(d.id) ? rels.set(d.id, []) : undefined; // noOp.;
if (!d.parentId) {
roots.push(d.id)
return;
}
const parent = rels.get(d.parentId) || [];
parent.push(d.id);
rels.set(d.parentId, parent);
});
function build(id) {
const data = store.get(id);
const children = rels.get(id);
if (children.length === 0) {
return {...data}
}
return {...data, children: children.map(c => build(c)) };
}
return roots.map(r => build(r));
}
const data = [{"id":"OS:MacOS","type":"OS","value":"MacOS"},{"parentId":"OS:MacOS","id":"Version:Catalina","type":"Version","value":"Catalina"},{"parentId":"Version:Catalina","id":"Browser:Chrome","type":"Browser","value":"Chrome"},{"id":"OS:Windows","type":"OS","value":"Windows"},{"parentId":"OS:Windows","id":"Version:7","type":"Version","value":"7"},{"parentId":"OS:MacOS","id":"Version:Mojave","type":"Version","value":"Mojave"},{"parentId":"Version:Mojave","id":"Browser:Chrome","type":"Browser","value":"Chrome"},{"parentId":"OS:Windows","id":"Version:XP","type":"Version","value":"XP"},{"parentId":"Version:XP","id":"Browser:Chrome","type":"Browser","value":"Chrome"}]
console.log(JSON.stringify(buildTree(data), null, 2))
Edit Note:
Earlier answer was class based. Removed that for simplicity. You can further optimize the space storage by changing store to be index based.
I need to convert this kind of array:
const obj = [{
name: 'firstLink',
type: 'topic',
id: 'ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6',
spacing: 1, // root
}, {
name: 'secondLink',
type: 'source',
id: 'd93f154c-fb1f-4967-a70d-7d120cacfb05',
spacing: 2, // child of previous object
}, {
name: 'thirdLink',
type: 'topic',
id: '31b85921-c4af-48e5-81ae-7ce45f55df81',
spacing: 1, // root
}]
Into this object:
const map = {
'ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6': {
name: 'firstLink',
type: 'topic',
children: {
'd93f154c-fb1f-4967-a70d-7d120cacfb05': {
name: 'secondLink',
type: 'source',
}
},
},
'31b85921-c4af-48e5-81ae-7ce45f55df81': {
name: 'thirdLink',
type: 'topic',
}
}
There might be up to 10 nestings, may be more (defined as spacing in the array).
How can i do that? I can use only pure js and lodash library.
You could use an array as reference to the inserted nested object.
var obj = [{ name: 'firstLink', type: 'topic', id: 'ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6', spacing: 1, }, { name: 'secondLink', type: 'source', id: 'd93f154c-fb1f-4967-a70d-7d120cacfb05', spacing: 2, }, { name: 'thirdLink', type: 'topic', id: '31b85921-c4af-48e5-81ae-7ce45f55df81', spacing: 1, }],
map = {};
obj.forEach(function (a) {
this[a.spacing - 1][a.id] = { name: a.name, type: a.type, children: {}};
this[a.spacing] = this[a.spacing - 1][a.id].children;
}, [map]);
console.log(map);
If you do not like empty children objects, you could use this proposal. It creates children properties only if necessary.
var obj = [{ name: 'firstLink', type: 'topic', id: 'ab75ca14-dc7c-4c3f-9115-7b1b94f88ff6', spacing: 1, }, { name: 'secondLink', type: 'source', id: 'd93f154c-fb1f-4967-a70d-7d120cacfb05', spacing: 2, }, { name: 'thirdLink', type: 'topic', id: '31b85921-c4af-48e5-81ae-7ce45f55df81', spacing: 1, }],
map = {};
obj.forEach(function (a) {
this[a.spacing - 1].children = this[a.spacing - 1].children || {};
this[a.spacing - 1].children[a.id] = { name: a.name, type: a.type};
this[a.spacing] = this[a.spacing - 1].children[a.id];
}, [map]);
map = map.children;
console.log(map);
I made dojo tree with checkbox.
but when I checked parent node, child node was not selected automatically.(not expanded child)
Only when I expand the tree once, after that, Child node can be selected when I click the parent checkbox.
Here is the code.
require([
"dojo/_base/window", "dojo/store/Memory",
"dijit/tree/ObjectStoreModel",
"dijit/Tree", "dijit/form/CheckBox","dojo/dom",
"dojo/domReady!"
], function(win, Memory, ObjectStoreModel, Tree, checkBox, dom){
// Create test store, adding the getChildren() method required by ObjectStoreModel
var myStore = new Memory({
data: [
{ id: 'world', name:'The earth', type:'planet', population: '6 billion'},
{ id: 'AF', name:'Africa', type:'continent', population:'900 million', area: '30,221,532 sq km',
timezone: '-1 UTC to +4 UTC', parent: 'world'},
{ id: 'EG', name:'Egypt', type:'country', parent: 'AF' },
{ id: 'KE', name:'Kenya', type:'country', parent: 'AF' },
{ id: 'Nairobi', name:'Nairobi', type:'city', parent: 'KE' },
{ id: 'Mombasa', name:'Mombasa', type:'city', parent: 'KE' },
{ id: 'SD', name:'Sudan', type:'country', parent: 'AF' },
{ id: 'Khartoum', name:'Khartoum', type:'city', parent: 'SD' },
{ id: 'AS', name:'Asia', type:'continent', parent: 'world' },
{ id: 'CN', name:'China', type:'country', parent: 'AS' },
{ id: 'IN', name:'India', type:'country', parent: 'AS' },
{ id: 'RU', name:'Russia', type:'country', parent: 'AS' },
{ id: 'MN', name:'Mongolia', type:'country', parent: 'AS' },
{ id: 'OC', name:'Oceania', type:'continent', population:'21 million', parent: 'world'},
{ id: 'EU', name:'Europe', type:'continent', parent: 'world' },
{ id: 'DE', name:'Germany', type:'country', parent: 'EU' },
{ id: 'FR', name:'France', type:'country', parent: 'EU' },
{ id: 'ES', name:'Spain', type:'country', parent: 'EU' },
{ id: 'IT', name:'Italy', type:'country', parent: 'EU' },
{ id: 'NA', name:'North America', type:'continent', parent: 'world' },
{ id: 'SA', name:'South America', type:'continent', parent: 'world' }
],
getChildren: function(object){
return this.query({parent: object.id});
}
});
// Create the model
var myModel = new ObjectStoreModel({
store: myStore,
query: {id: 'world'}
});
// Create the Tree.
var tree = new Tree({
model: myModel,
getIconClass:function(item, opened){
console.log('tree getIconClass', item, opened);
console.log('tree item type', item.type);
},
onClick: function(item, node) {
node._iconClass= "dijitFolderClosed";
node.iconNode.className = "dijitFolderClosed";
console.log(node.domNode.id);
var id = node.domNode.id, isNodeSelected = node.checkBox.get('checked');
console.log(isNodeSelected);
dojo.query('#'+id+' .dijitCheckBox').forEach(function(node){
dijit.getEnclosingWidget(node).set('checked',isNodeSelected);
});
},
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
console.log('Tree created', args);
var cb = new dijit.form.CheckBox({id: args.item.id});
cb.placeAt(tnode.labelNode, "first");
tnode.checkBox = cb;
return tnode;
}
});
tree.placeAt(contentHere);
tree.startup();
//tree.expandAll();
});
http://jsfiddle.net/pyz9Lcpv/9/
This happens because before a node is opened in a tree, it's contents do not exist yet. They're loaded after a node is opened up in a tree. What you can do is plug into the tree's onOpen event and then decide there whether you should check the checkboxes of all the nodes that were just created.
In my ASP.NET project I tried to use some dojo UI elements. My first attempt was a tree like it is shown in the dojo documentation. But the tree refuses to hold the state of the expanded nodes after refreshing the browser page.
require([
"dojo/ready", "dojo/_base/window", "dojo/store/Memory",
"dijit/tree/ObjectStoreModel", "dijit/Tree"
], function (ready, win, Memory, ObjectStoreModel, Tree) {
// Create test store, adding the getChildren() method required by ObjectStoreModel
var myStore = new Memory({
data: [
{ id: 'world', name: 'The earth', type: 'planet', population: '6 billion' },
{ id: 'AF', name: 'Africa', type: 'continent', parent: 'world'
},
{ id: 'EG', name: 'Egypt', type: 'country', parent: 'AF' },
{ id: 'KE', name: 'Kenya', type: 'country', parent: 'AF' },
{ id: 'Nairobi', name: 'Nairobi', type: 'city', parent: 'KE' },
{ id: 'Mombasa', name: 'Mombasa', type: 'city', parent: 'KE' },
{ id: 'SD', name: 'Sudan', type: 'country', parent: 'AF' },
{ id: 'Khartoum', name: 'Khartoum', type: 'city', parent: 'SD' },
{ id: 'AS', name: 'Asia', type: 'continent', parent: 'world' },
{ id: 'CN', name: 'China', type: 'country', parent: 'AS' },
{ id: 'IN', name: 'India', type: 'country', parent: 'AS' },
{ id: 'RU', name: 'Russia', type: 'country', parent: 'AS' },
{ id: 'MN', name: 'Mongolia', type: 'country', parent: 'AS' },
{ id: 'OC', name: 'Oceania', type: 'continent', population: '21 million', parent: 'world' },
{ id: 'EU', name: 'Europe', type: 'continent', parent: 'world' },
{ id: 'DE', name: 'Germany', type: 'country', parent: 'EU' },
{ id: 'FR', name: 'France', type: 'country', parent: 'EU' },
{ id: 'ES', name: 'Spain', type: 'country', parent: 'EU' },
{ id: 'IT', name: 'Italy', type: 'country', parent: 'EU' },
{ id: 'NA', name: 'North America', type: 'continent', parent: 'world' },
{ id: 'SA', name: 'South America', type: 'continent', parent: 'world' }
],
getChildren: function (object) {
return this.query({ parent: object.id });
}
});
// Create the model
var myModel = new ObjectStoreModel({
store: myStore,
query: { id: 'world' }
});
// Create the Tree. Note that all widget creation should be inside a dojo.ready().
ready(function () {
var tree = new Tree({
model: myModel,
persist: true
});
tree.placeAt("tree2");
tree.startup();
});
});
</script>
<div id="tree2"></div>
Cookies are activated. I want this to happen just on client side. Tried it with chrome, firefox and IE.
The id for the tree was missing. With id it works:
var tree = new Tree({
id: "yourtree",
model: myModel,
....