I made a function in order to find an element in a tree object. My function works, but sometimes the function don't find the value and stop before looking all the tree.
I can't explain why it works sometimes and sometimes not.
Here is my fiddle : http://jsfiddle.net/3cdwA/2/
When you click on categories like "Sciences" you can see that it works. But if you click on "Bandes-Dessinées" it should display "Comics" but it doesn't.
Here is my recursive function :
function getChildrenFromCurrentFolder(tree, targetFolder) {
console.log(tree);
// Find recursivly all direct children of targeted folder
if (targetFolder == tree.id) {
return tree.folders;
} else if (tree.folders.length > 0) {
var folders = [];
for (i = 0; folders.length == 0 && i < tree.folders.length; i++) {
folders = getChildrenFromCurrentFolder(tree.folders[i], targetFolder);
}
return folders;
}
return [];
}
here is my test tree :
tree = {
'id': 1,
'name': 'Mes Bookmarks',
'folders': [
{
'id': 2,
'name': 'Sciences',
'folders': [
{
'id': 3,
'name': 'Biologie',
'folders': [
{
'id': 12,
'name': 'Neurologie',
'folders': []
}
]
},
{
'id': 4,
'name': 'Astrophysique',
'folders': [
{
'id': 8,
'name': 'Cosmologie',
'folders': [
{
'id': 10,
'name': 'Système solaire',
'folders': []
}
]
},
{
'id': 9,
'name': 'Trous noirs',
'folders': []
}
]
},
{
'id': 5,
'name': 'Mathématiques',
'folders': []
}
]
},
{
'id': 6,
'name': 'Actualités',
'folders': [
{
'id': 11,
'name': 'Monde',
'folders': []
}
]
},
{
'id': 7,
'name': 'Bandes-dessinées',
'folders': [
{
'id': 13,
'name': 'Comics',
'folders': []
}
]
}
]
};
It's a simple, but common mistake. You forgot to declare your loop variables and that's trouble when doing recursion as you're creating a global that's being reused:
function displayFolders() {
...
for (var i = folders.length-1; i >= 0; i--)
... --^--
}
function getChildrenFromCurrentFolder(tree, targetFolder) {
...
for (var i = 0; folders.length == 0 && i < tree.folders.length; i++) {
... --^--
}
function getBreadcrumb(tree, targetFolder, breadcrumb) {
...
for (var i = 0; i < tree['folders'].length; i++)
... --^--
}
I'm not sure all the other logic is correct, but this definitely changes the behavior.
http://jsfiddle.net/3cdwA/4/
Related
I have an array of objects like the following:
const items = [
{
'name': 'P1',
'value': 50
},
{
'name': 'P1',
'value': 49
},
{
'name': 'P2',
'value': 50
},
{
'name': 'P3',
'value': 50
}
]
I need to add up the values and then push into an array of objects only if the 1 or more objects make up a value total of 100.
Expected output:
const groupedItems = [
[
{
'name': 'P1',
'value': 50
},
{
'name': 'P1',
'value': 49
},
],
[
{
'name': 'P2',
'value': 50
},
{
'name': 'P3',
'value': 50
}
]
]
What I have tried:
const temp = [];
for (var i = 0; i < items.length; i++) {
if ((items[i] + items[i + 1]) < 100) {
temp.push(items[i]);
}
}
groupedItems.push(temp);
I guess i would approch your solution like this :
Each time retrieving the first array, reducing it and if the sum of the array + the value of your item is less than 100, pushing your item in the array.
Otherwise, pushing a new array in your groupedItems that contains your item.
There might be a better way of doing this but this is the main idea.
const items = [{
'name': 'P1',
'value': 50
},
{
'name': 'P1',
'value': 49
},
{
'name': 'P2',
'value': 50
},
{
'name': 'P3',
'value': 50
}
]
const groupedItems = []
items.forEach(item => {
if (groupedItems.length > 0) {
const lastArray = groupedItems[groupedItems.length - 1]
const sumLastArray = lastArray.reduce((total, current) => current.value + total,
0)
if (sumLastArray + item.value < 100) {
lastArray.push(item)
} else {
groupedItems.push([item])
}
} else {
groupedItems.push([item])
}
})
console.log(groupedItems)
Suppose an array of objects:
arr1 = [
{ 'catId': 1, 'name': 'A' },
{ 'catId': 2, 'name': 'B' },
{ 'catId': 3, 'name': 'C' },
{ 'catId': 2, 'name': 'D' },
{ 'catId': 1, 'name': 'E' },
{ 'catId': 1, 'name': 'F' },
{ 'catId': 3, 'name': 'G' },
{ 'catId': 3, 'name': 'H' },
{ 'catId': 2, 'name': 'I' },
{ 'catId': 1, 'name': 'J' }
]
How can I randomly choose two Item of catId=1 and each Items from remaining category.
Required
arr2 = [
{ 'catId': 1, 'name': 'A' },
{ 'catId': 1, 'name': 'F' },
{ 'catId': 2, 'name': 'I' },
{ 'catId': 3, 'name': 'G' }
]
This is a very simple and naïve approach like I explained on my comment, but it gets the job done:
var arr1 = [
{ 'catId': 1, 'name': 'A' },
{ 'catId': 2, 'name': 'B' },
{ 'catId': 3, 'name': 'C' },
{ 'catId': 2, 'name': 'D' },
{ 'catId': 1, 'name': 'E' },
{ 'catId': 1, 'name': 'F' },
{ 'catId': 3, 'name': 'G' },
{ 'catId': 3, 'name': 'H' },
{ 'catId': 2, 'name': 'I' },
{ 'catId': 1, 'name': 'J' }
];
//shuffles an array
function shuffle(arr) {
return arr.sort(() => Math.random() - 0.5);
}
//gets items with catId = 1, and shuffles them
var cat1 = shuffle(arr1.filter(function(item) {
return item.catId == 1;
}));
var otherCat = [];
//pushes items in the otherCat array that aren't catId = 1, and not duplicate category
for (var i = 0; i < arr1.length; i++) {
//if not catId = 1 and not already in array
if (arr1[i].catId != 1 && !find(arr1[i])) {
//get all items in this category, and shuffles them to get a random item
var thisCat = shuffle(arr1.filter(function(item) { return item.catId == arr1[i].catId; }))[0];
otherCat.push(thisCat);
}
}
//finds an item in otherCat array by catId
function find(item) {
return otherCat.find(function(i) {
return item.catId === i.catId;
});
}
var result = [];
result.push(cat1[0]);
result.push(cat1[1]);
//concatenate both arrays
Array.prototype.push.apply(result, otherCat);
console.log(JSON.stringify(result));
I coded it this way because it is very simple to see. You could in theory loop through the whole array once to get all catId = 1 and other catId into 2 different arrays (I know I am doing multiple passes to the array, but like I said, this is just so you can get the idea).
Another way of doing it (perhaps a little more complex) is by grouping the items by category, then looping thru each category and grabbing a random element (2 in the case of catId == 1):
var arr1 = [
{ 'catId': 1, 'name': 'A' },
{ 'catId': 2, 'name': 'B' },
{ 'catId': 3, 'name': 'C' },
{ 'catId': 2, 'name': 'D' },
{ 'catId': 1, 'name': 'E' },
{ 'catId': 1, 'name': 'F' },
{ 'catId': 3, 'name': 'G' },
{ 'catId': 3, 'name': 'H' },
{ 'catId': 2, 'name': 'I' },
{ 'catId': 1, 'name': 'J' }
];
//groups by a property
//https://stackoverflow.com/a/34890276/752527
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
//shuffles an array
function shuffle(arr) {
return arr.sort(() => Math.random() - 0.5);
}
var result = [];
var grouped = groupBy(arr1, 'catId');
var keys = Object.keys(grouped);
for (var i = 0; i < keys.length; i++) {
var group = grouped[keys[i]]
//if i have items in my group, shuffle them and grab 1, or 2 items from it
if (group && group.length > 0) {
var cat = shuffle(group);
result.push(cat[0]);
//adds the second item with catId ==1
if (group[0].catId === 1) {
result.push(cat[1]);
}
}
}
console.log(JSON.stringify(result));
If you want to return a list of n-number of items from a particular category and one from the remaining categories, then you could group the items by their catId and then map the entries to a randomized count (length) based on whether the current key is the chosen bias.
Edit: I added a bias to keep n-number of items from the category of choice.
const categories = [
{ 'catId': 1, 'name': 'A' }, { 'catId': 2, 'name': 'B' },
{ 'catId': 3, 'name': 'C' }, { 'catId': 2, 'name': 'D' },
{ 'catId': 1, 'name': 'E' }, { 'catId': 1, 'name': 'F' },
{ 'catId': 3, 'name': 'G' }, { 'catId': 3, 'name': 'H' },
{ 'catId': 2, 'name': 'I' }, { 'catId': 1, 'name': 'J' }
];
const sortFn = (
{ catId: ai, name: an },
{ catId: bi, name: bn }
) =>
(ai - bi) || an.localeCompare(bn);
const main = () => {
print(pickRand(categories, ({ catId }) => catId, 1, 2).sort(sortFn));
};
const shuffle = (arr) => arr.sort(() => Math.random() - 0.5);
const grouped = (arr, keyFn) => arr.reduce((acc, item, idx) =>
({ ...acc, [keyFn(item)]: [...(acc[keyFn(item)] ?? []), idx] }), {});
const pickRand = (arr, keyFn, bias, count) =>
Object
.entries(grouped(arr, keyFn))
.flatMap(([key, idx]) =>
shuffle(idx).slice(0, bias == key ? count : 1)
.map(i => arr[i]));
const print = (arr) => console.log(arr.map(x => JSON.stringify(x)).join('\n'));
main();
.as-console-wrapper { top: 0; max-height: 100% !important; }
I have a an array containing some values. I need to populate values dynamically using the array elements.
Below is the master array.
list = [{name: 'm1'}, {name: 'm2'},{name: 'm3'},{name: 'm4'},{name: 'm5'},]
I have JSON called demo.json
demo: {
listCard = {
'x': 0,
'y': 1,
'name': ''
},
layout: [
{
'col': '3',
'row': '3'
'grids': []
}
]
};
The result should be -
demo: {
listCard = {
'x': 0,
'y': 1,
'name': ''
},
layout: [
{
'col': '3',
'row': '3'
'grids': [
{
'x': 0,
'y': 1,
'name': 'm1'
},
{
'x': 0,
'y': 1,
'name': 'm2'
}
]
},
{
'col': '3',
'row': '3'
'grids': [
{
'x': 0,
'y': 1,
'name': 'm3'
},
{
'x': 0,
'y': 1,
'name': 'm4'
}
]
},
{
'col': '3',
'row': '3'
'grids': [
{
'x': 0,
'y': 1,
'name': 'm5'
}
]
}
]
};
Basically the result should be like demo.listcard.name should iterate list.length times & get each value. After that layout.grid should be assigned with the whole object of listcard with 2 objects.
Below is the method I used but I am constantly failing.
let listcard = demo.listcard; //here I get listcard object
const layout = demo.layout; // layout object
const noOfScreens = (arr, size) => arr.reduce((acc, e, i) => (i % size ? acc[acc.length - 1].push(e) : acc.push([e]), acc), []);
const screens = noOfScreens(list, 2); // master array is split into arrays of size 2.
for (let i = 0; i < screens.length; i++) {
layout[0].grids.push(layout);
}
I am stuck here. Please help me out
Here is what you want:
const demo = {
listCard: {
'x': 0,
'y': 1,
'name': ''
},
layout: []
};
const list = [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }, { name: 'm4' }, { name: 'm5' }]
for (var i = 0; i < list.length; i++) {
if (i % 2 == 0) {
demo.layout.push({
'col': '3',
'row': '3',
'grids': [{
'x': 0,
'y': 1,
'name': list[i].name
}]
})
} else {
demo.layout[Math.floor(i / 2)].grids.push({
'x': 0,
'y': 1,
'name': list[i].name
});
}
}
console.log(demo);
I have array of objects in object have different key so i want the object having minimum value in that array
list = [{
'id': 4,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 1,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 2,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 3,
'name': 'nitin',
'group': 'angularjs'
}
]
I tried by
var minValue = Math.min.apply(Math, list.map(function (o) {
return o.id;
}))
but it returns only the id not whole object then i have to make more filter for get object
is there any direct method to find object?
You can use Array reduce method:
var list = [
{
'id':4,
'name':'nitin',
'group':'angularjs'
},
{
'id':1,
'name':'nitin',
'group':'angularjs'
},
{
'id':2,
'name':'nitin',
'group':'angularjs'
},
{
'id':3,
'name':'nitin',
'group':'angularjs'
}];
var result = list.reduce(function(res, obj) {
return (obj.id < res.id) ? obj : res;
});
console.log(result);
Using Array#reduce()
list = [{
'id': 4,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 1,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 2,
'name': 'nitin',
'group': 'angularjs'
},
{
'id': 3,
'name': 'nitin',
'group': 'angularjs'
}
];
let min = list.reduce((prev, curr) => prev.id < curr.id ? prev : curr);
console.log(min);
I tried these solutions with Array.reduce but none of them were full proof. They do not handle cases where the array is empty or only has 1 element.
Instead this worked for me:
const result = array.reduce(
(prev, current) =>
(prev?.id ?? current?.id) >= current?.id ? current : prev,
null,
);
It returns null if array is empty and handles other cases well.
You can check for the object with the lowest id by looping through the array like so, probably not the neatest way, but a solution either way:
var list = [
{
'id':4,
'name':'nitin',
'group':'angularjs'
},
{
'id':1,
'name':'nitin',
'group':'angularjs'
},
{
'id':2,
'name':'nitin',
'group':'angularjs'
},
{
'id':3,
'name':'nitin',
'group':'angularjs'
}]
var tmp = list[0].id;
list.forEach(function (entry) {
if (entry.id < tmp) {
tmp = entry;
}
});
console.log(tmp);
https://jsfiddle.net/camgssk3/
other option may be to filter your array as you already have min "id"
var list = [
{
'id':4,
'name':'nitin',
'group':'angularjs'
},
{
'id':1,
'name':'nitin',
'group':'angularjs'
},
{
'id':2,
'name':'nitin',
'group':'angularjs'
},
{
'id':3,
'name':'nitin',
'group':'angularjs'
}];
var min = Math.min.apply(Math, list.map(function (o) {
return o.id;
}));
filtered = list.filter(function(elem){
if(elem.id == min){console.log(elem);return elem;}
})
I'm trying to find objects in an array that are the same to flag them in the UI. I can't seem to use undescore to do it.
I was doing this:
var a = [ {'id': 1, 'name': 'jake' }, {'id':4, 'name': 'jenny'}, {'id': 9, 'name': 'nick'}, {'id': 1, 'name': 'jake' } ];
var eaches = _.each(a, function (obj) {
_.find(a, function () {
return _.isEqual(a, obj);
});
});
Thanks in advance!
Seems you need something like this:
var a = [{
'id': 1,
'name': 'jake'
}, {
'id': 4,
'name': 'jenny'
}, {
'id': 9,
'name': 'nick'
}, {
'id': 1,
'name': 'jake'
}];
var eq = [];
_.each(a, function (x, i) {
var e = _.find(a, function (y, j) {
return i !== j && _.isEqual(x, y);
});
if (e) {
eq.push(x);
}
});
console.log(eq);
http://jsfiddle.net/f0t0n/WBbs5/
UPDATE:
Custom "_.uniq" based on _.isEqual instead of === strict comparison:
var uniqEq = _.reject(eq, function(x, i) {
return _.find(eq, function(y, j) {
return i < j && _.isEqual(x, y);
});
});
http://jsfiddle.net/f0t0n/hzBBA/