Related
I have an array of nested regions that look like this:
Egypt
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
The array itself:
[
{
"key": 10,
"title": "Egypt",
"parent_key": null,
"children": [
{
"key": 1,
"title": "Zone 1",
"parent_key": 10,
"children": [
{
"key": 3,
"title": "Tagamo3",
"parent_key": 1,
"children": []
},
{
"key": 7,
"title": "Giza",
"parent_key": 1,
"children": []
},
{
"key": 8,
"title": "Helwan",
"parent_key": 1,
"children": []
},
{
"key": 11,
"title": "Fayoum",
"parent_key": 1,
"children": []
}
]
},
{
"key": 2,
"title": "Zone 2",
"parent_key": 10,
"children": [
{
"key": 4,
"title": "Gesr ElSuis",
"parent_key": 2,
"children": [
{
"key": 12,
"title": "test",
"parent_key": 4,
"children": []
}
]
},
{
"key": 5,
"title": "Delta",
"parent_key": 2,
"children": []
},
{
"key": 6,
"title": "Mohandeseen",
"parent_key": 2,
"children": []
},
{
"key": 9,
"title": "Down Town",
"parent_key": 2,
"children": []
}
]
}
]
}
]
I want to return to the highest region in a given input
Examples:
input [7, 1, 10] should return [10] since 10 is Egypt parent of 1 and 7
input [1, 2] should return both [1, 2] since they are on the same level both Zone 1 and zone 2 located under Egypt
input [2, 3, 1] should return [2, 1] since they are on the same level and 3 removed because it's a child of 1
input [1, 4] should return [1, 4] since they are on different levels and no one parent to the other
First it helps to turn your tree structure into a map of descendant ids, recursively:
const descendantsMap = new Map<number, Set<number>>();
function walk(tree: Tree) {
const s: Set<number> = new Set();
descendantsMap.set(tree.key, s);
for (const t of tree.children) {
walk(t);
s.add(t.key);
descendantsMap.get(t.key)?.forEach(v => s.add(v));
}
}
arr.forEach(walk);
We are building up a Map from each key in your tree structure to a Set of the keys of its descendants. The walk() function is recursive, and we merge the descendants for the children of each node into the descendants for the current node.
Let's make sure it looks right:
console.log(descendantsMap);
/* Map (12) {
10 => Set (11) {1, 3, 7, 8, 11, 2, 4, 12, 5, 6, 9},
1 => Set (4) {3, 7, 8, 11},
3 => Set (0) {},
7 => Set (0) {},
8 => Set (0) {},
11 => Set (0) {},
2 => Set (5) {4, 12, 5, 6, 9},
4 => Set (1) {12},
12 => Set (0) {},
5 => Set (0) {},
6 => Set (0) {},
9 => Set (0) {}
} */
Yes. You can see how now we have a quick mapping from each key to the set of keys in its descendant subtree.
Now to get the "highest" entries in an array (I would call these the "shallowest" since they are closest to the root), we find all the descendants of all the elements in the array and then filter these out of the array:
const shallowest = (x: number[]): number[] => {
const descendants = new Set<number>();
for (const v of x) {
descendantsMap.get(v)?.forEach(i => descendants.add(i));
}
console.log(descendants); // just to understand what's happening
return x.filter(v => !descendants.has(v));
}
Let's test it:
console.log(shallowest([7, 1, 10]));
// descendants are {3, 7, 8, 11, 1, 2, 4, 12, 5, 6, 9}
// output [10]
console.log(shallowest([1, 2]));
// descendants are {3, 7, 8, 11, 4, 12, 5, 6, 9};
// output [1, 2]
console.log(shallowest([2, 3, 1]));
// descendants are {4, 12, 5, 6, 9, 3, 7, 8, 11};
// output [2, 1]
console.log(shallowest([1, 4]));
// descendants are {3, 7, 8, 11, 12};
// output [1, 4]
Looks good. You can see that shallowest([7, 1, 10]) first finds all the descendants of 7, 1, and 10, which is {3, 7, 8, 11, 1, 2, 4, 12, 5, 6, 9}, or everything except 10. So when we filter those out of [7, 1, 10] we are left with just 10. Similarly, shallowest([1, 2]) and shallowest([1, 4]) produce sets of descendants that don't overlap at all with the input, so the output is identical to the input. And with shallowest([2, 3, 1]), the list of descendants contains 3 but not 2 or 1, so the output is [2, 1].
Playground link to code
This is my 2nd attempt, thanks to jcalz for pointing out the error and his solution is neater than mine.
The function buildArray builds an array of objects in to the variable keyArray, the key is the element in the array to be searched and another array that's the path to that element (so key 7 will have a path of [10, 1, 7]).
We then filter keyArray to remove any elements that have a parent in the original search array.
Anyway, reading jcalz's solution, I've learnt about maps so my time's not been entirely wasted. Hope this helps in some way though.
console.log(search2([7, 1, 10], obj)); //returns [10]
console.log(search2([1,2], obj)); //returns [1,2]
console.log(search2([2,3,1], obj)); //returns [1,2]
console.log(search2([1,4], obj)); //returns [1,4]
function search2(search, obj) {
keyArray=[];
buildArray(obj);
return keyArray.filter((element)=> !element.path.some(e => search.includes(e))).map((e)=> e.key);
function buildArray(obj, path=[]) {
obj.forEach((element) =>{
if(search.includes(element.key)) {
keyArray.push({"key":element.key,"path":path});
}
buildArray(element.children,[...path,element.key]);
});
}
}
I am trying to make a javaScript game of housie/bingo game. I used the library npm tambola-generator which returns auto generated tickets.
The server side process generates an array similar to this one ...
[{
"_entries": [[0,17,0,0,41,53,0,78,86], [4,0,0,35,0,58,67,80,0]/*, [ ... ] */],
}, {
"_entries": [[0,16,23,0,41,51,0,0,88], [2,20,0,31,43,56,0,0,0]/*, [ ... ] */],
}, {
"_entries": [[0,0,23,33,0,51,0,73,87], [1,0,0,35,42,58,68,0,0]/*, [ ... ] */],
}, {
// ... more items like that ...
}]
I want each ticket to feature a serial number which should be equal to a ticket items's array index but starting with the base of 1 instead of 0.
How does one include such a ticket number so that I can manage tickets for players?
I am just a beginner I couldn't solve it out. The target structure of the above example code should look like the following one ...
[{
"ticketNum": 1,
"_entries": [[0,17,0,0,41,53,0,78,86], [4,0,0,35,0,58,67,80,0]/*, [ ... ] */],
}, {
"ticketNum": 2,
"_entries": [[0,16,23,0,41,51,0,0,88], [2,20,0,31,43,56,0,0,0]/*, [ ... ] */],
}, {
"ticketNum": 3,
"_entries": [[0,0,23,33,0,51,0,73,87], [1,0,0,35,42,58,68,0,0]/*, [ ... ] */],
}, {
// ... more items like that ...
}]
Just use Array.prototype.map for achieving this task.
const entries = [{
"_entries": [
[0, 17, 0, 0, 41, 53, 0, 78, 86],
[4, 0, 0, 35, 0, 58, 67, 80, 0]
],
}, {
"_entries": [
[0, 16, 23, 0, 41, 51, 0, 0, 88],
[2, 20, 0, 31, 43, 56, 0, 0, 0]
],
}, {
"_entries": [
[0, 0, 23, 33, 0, 51, 0, 73, 87],
[1, 0, 0, 35, 42, 58, 68, 0, 0]
],
}];
const tcktEntries = entries.map((entry, index) => {
return {
ticketNum: index + 1,
...entry
};
});
console.log({
tcktEntries
});
.as-console-wrapper {
min-height: 100%!important;
top: 0;
}
I want to run multiple backtests on certain stock data. I want to do this by generating an array of strategy options that I will pass to a backtest function.
In the object below I can define values as arrays so that multiple combinations of strategies will be formed.
The amount of combinations of [0,1] and [2,3] is 4, that's why my output array will consist of 4 strategy objects.
To illustrate, this is my (simplified) input:
const backtestSettings = {
stopLoss: [5],
bands: [
{
timeframe: 1,
openMinVolatility: [0,1],
},
{
timeframe: 5,
openMinVolatility: [2,3],
},
{
timeframe: 15,
openMinVolatility: [0],
},
{
timeframe: 30,
openMinVolatility: [0],
}
]
};
And I am trying to get this as my output:
[
{
stopLoss: 5,
bands: [
{
timeframe: 1,
openMinVolatility: 0
},
{
timeframe: 5,
openMinVolatility: 2
},
{
timeframe: 15,
openMinVolatility: 0
},
{
timeframe: 30,
openMinVolatility: 0
}
]
},
{
stopLoss: 5,
bands: [
{
timeframe: 1,
openMinVolatility: 1
},
{
timeframe: 5,
openMinVolatility: 2
},
{
timeframe: 15,
openMinVolatility: 0
},
{
timeframe: 30,
openMinVolatility: 0
}
]
},
{
stopLoss: 5,
bands: [
{
timeframe: 1,
openMinVolatility: 0
},
{
timeframe: 5,
openMinVolatility: 3
},
{
timeframe: 15,
openMinVolatility: 0
},
{
timeframe: 30,
openMinVolatility: 0
}
]
},
{
stopLoss: 5,
bands: [
{
timeframe: 1,
openMinVolatility: 1
},
{
timeframe: 5,
openMinVolatility: 3
},
{
timeframe: 15,
openMinVolatility: 0
},
{
timeframe: 30,
openMinVolatility: 0
}
]
}
]
Question: How do I convert my input to the desired output? (I've spent days trying a lot of different things)
Bonus: I have something working without the bands (the fact that it is nested makes things complicated for me) by chaining some forEach functions, but in reality the amount of options is substantial, which makes my code really long and unreadable. So I'm hoping that there is a solution that can also accept an arbitrary amount of options instead of chaining forEach functions.
Since you're looking for a hint rather than a full solution, one (non-recursive) helper function might give you a start:
const crossproduct = (xss) =>
xss .reduce (
(ps, xs) => ps .reduce ((r, p) => [... r, ... (xs .map ((x) => [... p, x]))], []),
[[]]
)
const as = [1, 2], bs = ['a', 'b', 'c'], cs = ['x'], ds = ['y', 'z']
console .log (crossproduct ([as, bs, cs, ds]))
This takes the generalized Cartesian product of an array of arrays. That seems an essential step in your process.
I need to compare id objects array from a firstArray and array numbers (secondArray) and return a new array with objects from the first array which id number exists in the second array.
So at the end, I want a new array with objects with id 39 and 41.
Actually I find something like this:
const result = arr2.filter(o => arr1.find(x => x.id === o));
const arr1 =
"blocks": [
{
"id": 1,
"functions": [ 0, 1 ]
},
{
"id": 39,
"functions": [ 0, 1, 3, 4 ]
},
{
"id": 41,
"functions": [ 0, 1 ]
}
]
const arr2 = [39, 41]
You can use includes() function during filtering. Includes() works like in array function.
const arr1 =
[ {
"id": 1,
"functions": [ 0, 1 ] },
{
"id": 39,
"functions": [ 0, 1, 3, 4 ]
},
{
"id": 41,
"functions": [ 0, 1 ]
}
]
const arr2 = [39, 41]
const result = arr1.filter(o => arr2.includes(o.id));
console.log(result)
You can create a Map to see whether there is an item of Map exists in filtering array. Getting an item from Map method is O(1):
const blocks = [
{
"id": 1,
"functions": [
0,
1
]
},
{
"id": 39,
"functions": [
0,
1,
3,
4
]
},
{
"id": 41,
"functions": [
0,
1
]
}
];
const arr2 = [39, 41];
const arr2Maps = new Map(arr2.map(a=>[a, a]));
const result = blocks.filter(o => arr2Maps.get(o.id));
console.log(result)
In addition, you can use filter and some methods. However, some method has O(n):
const blocks = [
{
"id": 1,
"functions": [
0,
1
]
},
{
"id": 39,
"functions": [
0,
1,
3,
4
]
},
{
"id": 41,
"functions": [
0,
1
]
}
];
const arr2 = [39, 41]
const result = blocks.filter(o => arr2.some(a=> a ==o.id ));
console.log(result)
I'm querying Firebase to get some data to throw into Chart.js. Here's how I've laid out my data:
{
"20160428": {
"follow": 13,
"host": 6,
"raid": 1,
"substreak": 1,
"tip": 1
},
"20160429": {
"follow": 15,
"host": 21,
"raid": 2,
"substreak": 4
},
"20160430": {
"follow": 4
},
"20160501": {
"follow": 11,
"host": 15,
"subscription": 4,
"substreak": 5
},
"20160502": {
"follow": 2,
"host": 6,
"subscription": 1,
"substreak": 4
},
"20160503": {
"follow": 2
}
}
As you can see, each object is keyed off by a timestamp and events don't always appear in every object (but there are a finite number of events). Here's how I'd like the data to look so I can feed it into Chart.js:
labels: ["20160428", "20160429", "20160430", ...]
{
"follow": [13, 15, 4, 11, 2, 2],
"host": [6, 21, 0, 15, 6, 0],
"raid": [1, 2, 0, 0, 0, 0],
"subscription": [0, 0, 0, 4, 1, 0]
"substreak": [1, 4, 0, 5, 4, 0]
"tip": [1, 0, 0, 0, 0, 0]
}
I've played with Lodash's groupBy and similar functions, but I'm not sure if I'm on the right track. I wouldn't mind doing this x times either per event, but at this point I can't change the schema.
If you have a defined set of keys that you want to group then you have to:
Use map() to pluck values with compact() to get non-null values from the object collection from the defined set of keys.
Build the result using zipObject() from the defined keys and values obtained from the first step.
var keys = ["follow", "host", "raid", "substreak", "tip", "subscription"];
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);
var data = {
"20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 },
"20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 },
"20160430": { "follow": 4 },
"20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 },
"20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 },
"20160503": { "follow": 2 }
};
var keys = ["follow", "host", "raid", "substreak", "tip", "subscription"];
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>
If you want to group them from all the keys present in the object collection then you can:
Get all the unique keys by:
map() each data object inside the object collection
flatten() the resulting array
Use uniq() to get all unique keys from the flattened array.
Use the methodology in the first example to get the values and build the object.
var keys = _(data).map(_.keys).flatten().uniq().value();
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);
var data = {
"20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 },
"20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 },
"20160430": { "follow": 4 },
"20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 },
"20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 },
"20160503": { "follow": 2 }
};
var keys = _(data).map(_.keys).flatten().uniq().value();
var values = _.map(keys, key => _(data).map(key).compact().value());
var result = _.zipObject(keys, values);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>
You could use plain javascript and some loops.
var data = { "20160428": { "follow": 13, "host": 6, "raid": 1, "substreak": 1, "tip": 1 }, "20160429": { "follow": 15, "host": 21, "raid": 2, "substreak": 4 }, "20160430": { "follow": 4 }, "20160501": { "follow": 11, "host": 15, "subscription": 4, "substreak": 5 }, "20160502": { "follow": 2, "host": 6, "subscription": 1, "substreak": 4 }, "20160503": { "follow": 2 } },
grouped = {}
Object.keys(data).forEach(function (k) {
["follow", "host", "raid", "substreak", "tip"].forEach(function (kk) {
grouped[kk] = grouped[kk] || [];
grouped[kk].push(data[k][kk] || 0);
});
});
document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');
Don't try this at home.
var fields = {
follow: 0,
host: 0,
raid: 0,
substreak: 0,
tip: 0,
subscription: 0
};
_(data)
.values()
.map(x => _.assign({}, fields, x))
.map(_.toPairs)
.flatten()
.groupBy(0)
.mapValues(x => _.map(x, 1))
.value();