Let's assume I have the following data structure:
const Table = [
[
{
"id": "258ce34d-cba6-44a8-bdb9-e436d18701aa",
"seat": 1,
"group": 1
},
{
"id": "60adc321-c7e3-4d34-963a-e09dc53345d0",
"seat": 2,
"group": 1
}
],
[
{
"seat": "empty",
"group": 0
},
{
"seat": "empty",
"group": 0
}
],
[
{
"id": "c8c3c973-351b-4314-8096-a6d12c7b01fb",
"seat": 5,
"group": 3
},
{
"id": "1c256b45-b3f3-49cc-b7e4-29967594c4fb",
"seat": 6,
"group": 3
}
],
[
{
"seat": "empty",
"group": "empty"
},
{
"seat": 0,
"group": 0
}
],
[
{
"id": "63469f95-7deb-483c-ad7d-cf0cbdc191b1",
"seat": 9,
"group": 5
},
{
"id": "e77c8fb3-2e0b-43f7-a9ca-1bbd8143ba59",
"seat": 10,
"group": 5
}
]
]
It is an array that represents a table in a restaurant. In this array there are other arrays representing the groups of guests.
As you can see there are two free slots at the table.
The task now is to write a function that calculates how many seats are free in a row. Input is this array and output should be a number. So in this case MaxFreeSeatsinRow(Table) => 2
For example, if a group of 4 people arrives, there are enough seats but not in one row.
Does anyone have an idea how to calculate this? Thanks for your help!
According to your comments, this is really just a matter of flattening the structure to an array of seats and then finding the longest streak of empty seats.
We can write a general-purpose longestStreak function which accepts a predicate function and returns a function which accepts a list of elements, and then tests each element against that predicate, updating the length of the current streak and possibly the maximum value when it matches, and resetting the current streak to zero when it doesn't.
Our main function, canSeat accepts a Table, and supplies to longestStreak a predicate which tests if a seat has value of 0 or "empty" (note: do you really want to support both?) and then supplies to the resulting function an extract of the Table selecting all the seats in it.
const longestStreak = (pred) => (xs) =>
xs .reduce (
({max, curr}, x) => pred (x)
? {max: curr >= max ? curr + 1 : max, curr: curr + 1}
: {max, curr: 0}
, {max: 0, curr: 0}
) .max
const canSeat = (table) => longestStreak
(s => s == 0 || s == "empty")
(table .flatMap (groups => groups .map (g => g .seat)))
const Table = [[{id: "258ce34d-cba6-44a8-bdb9-e436d18701aa", seat: 1, group: 1}, {id: "60adc321-c7e3-4d34-963a-e09dc53345d0", seat: 2, group: 1}], [{seat: "empty", group: 0}, {seat: "empty", group: 0}], [{id: "c8c3c973-351b-4314-8096-a6d12c7b01fb", seat: 5, group: 3}, {id: "1c256b45-b3f3-49cc-b7e4-29967594c4fb", seat: 6, group: 3}], [{seat: "empty", group: "empty"}, {seat: 0, group: 0}], [{id: "63469f95-7deb-483c-ad7d-cf0cbdc191b1", seat: 9, group: 5}, {id: "e77c8fb3-2e0b-43f7-a9ca-1bbd8143ba59", seat: 10, group: 5}]]
console .log (canSeat (Table))
If I understood correctly, you'd like to know of the number of free seats per nested array?
If so, I think it can be pretty straightforward using a functional approach:
const freeSeats = Table.map(row => {
return row.filter(place => place.seat === "empty").length
})
// freeSeats = [0, 2, 0, 2, 0]
Then it's easy to get the maximum grouped seats:
Math.max(...freeSeats)
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 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.
Imagine I've got an array of objects like so (dummy code):
const chosenBets = [{...}, {...}] // 2 items
And I want to delete a specific item from the array:
{id: 0, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0,}
So that array is now:
const chosenBets = [{...}] // 1 items
How would I achieve this?
You can use array filter
const chosenBets = [{
id: 0, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0
}, {
id: 1, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0
}];
const filteredData = chosenBets.filter(item => item.id === 1);
console.log(filteredData);
You can use splice
var a = [{
id: 0, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0,
},
{
id: 0, // is unique
label: 11,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0,
}
]
a.forEach((e, j) => {
if (e.label == 1)
a.splice(j, 1);
})
console.log(a)
If you want to delete a specific and unique object, I would do something like:
let chosenBets = [{
id: 0, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0
}, {
id: 1, // is unique
label: 1,
odd: 1.33,
oddIndex: 0,
team_home: "Liverpool",
team_away: "Sheffield United",
matchCardIndex: 0
}];
let index = chosenBets.findIndex(({id}) => id === 1);
// if you want to mutate the original array, otherwise use `slice`
chosenBets.splice(index, 1);
console.log(chosenBets);
filter is better if you want to remove a group of elements, not just one. The reason is, it keeps going to iterate all the elements of the array, so it always iterate the whole array, even if the element you want to remove is the first one.
Using findIndex you just iterate until you find the element, and then return the index: so as average, it does less cycle.
From a given data structure (json file) I basically need to render a table. Empty rows and/or columns should not render. I'm fairly new to JavaScript and tried different approaches (converting to array and using .map(), reduce(), .filter(), lodash etc.) without success. I don't even know what the best way would be to tackle the problem. (Or what possible search terms would be.)
Neither "row keys" (In example: mo, tu, we, th, fr) nor "column keys" (john, hane, doe) are known and can vary.
Complete example: https://jsbin.com/rafeyasena/edit?js,output
"groupA": {
"mo": { "john": 8, "jane": 5, "doe": null },
"tu": { "john": 8, "jane": 5, "doe": null },
"we": { "john": 5, "jane": 9, "doe": null },
"th": { "john": 6, "jane": 3, "doe": null },
"fr": { "john": null, "jane": null, "doe": null }
}
Possible resulting data structure
const header = ["John", "Jane"];
const content = [
"mo": {[ 8, 5 ]},
"tu": {[ 8, 5 ]},
"we": {[ 5, 9 ]},
"th": {[ 6, 3 ]}
]
Expected result (Front-end, React):
| John | Jane |
---|------|--------
mo | 8 | 5 |
tu | 8 | 5 |
we | 5 | 9 |
th | 6 | 3 |
What I tried so far:
I was able to delete all values of null and the corresponding key, if it doesn't contain keys/values any more (Delete null values in nested javascript objects) - leading me with the challenge to find out all the leftover keys to build the table header. (In the example below this would be only John and Jane - So basically a way to iterate over all keys and log each key that exists at least one time). So my current data looks like this (but I'm not sure if it is the best way):
"groupA": {
"mo": { "john": 8, "jane": 5, },
"tu": { "john": 8, "jane": 5, },
"we": { "john": 5, "jane": 9, },
"th": { "john": 6, "jane": 3, }
}
I would just represent the data as a 2D array (that makes rendering easier):
const columnNames = [""];
const rows = [columnNames];
for(const [rowName, values] of Object.entries(groupA)) {
const row = [rowName];
for(const [columnName, value] of Object.entries(values)) {
let position = columnNames.indexOf(columnName);
if(value === null) continue;
if(position === -1)
position = columnNames.push(columnName) - 1;
row[position] = value;
}
rows.push(row);
}
// just some debugging:
console.log( rows.map(row => row.map(it => (it || "").padStart(10)).join("|")).join("\n") );
I think creating that latter format (with the nulls removed) is a very useful first step. From there you could write something like this to get it into a variant of your target format:
const uniqKeys = (obj) => [... new Set(Object.values(obj).flatMap(Object.keys))]
const transform = (group, headers = uniqKeys(group)) => ({
headers,
content: Object.entries(group).reduce(
(a, [k, v]) => ({...a, [k]: headers.map(h => v[h])}),
{}
)
})
const groupA = {mo: {john: 8, jane: 5}, tu: {john: 8, jane: 5}, we: {john: 5, jane: 9}, th: {john: 6, jane: 3}}
console.log(transform(groupA))
Note that the target is a little different than your request, as your example content isn't legal JS ({[ 8, 5 ]} doesn't make sense) but I think it captures the spirit of it, returning something like:
{
headers: ['john', 'jane'],
content: {
mo: [8, 5],
tu: [8, 5],
we: [5, 9],
th: [6, 3]
}
}
Note that this function is a little more general than the requirements, as you could supply it a list of headers and only extract those from the data.
Take a look at object-scan. It makes this sort of this relatively easy once you wrap your head around how it works. Here is how you'd answer your questions
// const objectScan = require('object-scan');
const isNullObject = (obj) => (
obj instanceof Object
&& !Array.isArray(obj)
&& Object.values(obj).every((e) => e === null)
);
const prune = (data) => objectScan(['**'], {
rtn: 'count',
filterFn: ({ value, parent, property }) => {
if (isNullObject(value)) {
delete parent[property];
return true;
}
return false;
}
})(data);
const stats = { groupA: { mo: { john: 8, jane: 5, doe: null }, tu: { john: 8, jane: 5, doe: null }, we: { john: 5, jane: 9, doe: null }, th: { john: 6, jane: 3, doe: null }, fr: { john: null, jane: null, doe: null } } };
console.log(prune(stats)); // return number of replaces
// => 1
console.log(stats);
/* =>
{ groupA:
{ mo: { john: 8, jane: 5, doe: null },
tu: { john: 8, jane: 5, doe: null },
we: { john: 5, jane: 9, doe: null },
th: { john: 6, jane: 3, doe: null } } }
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
I've got an array like that:
const arr = [
[{rivals: ['player1','player2'], winner: "player1", player1Scored: 2, player2Scored: 1}],
[{rivals: ['player1','player3'], winner: "none", player1Scored: 2, player3Scored: 2}],
[{rivals: ['player2','player3'], winner: "player3", player2Scored: 1, player3Scored: 3}],
[{rivals: ['player1','player4'], winner: "none", player1Scored: 1, player4Scored: 1}]
]
I need to count scored points of every player, so it'll look like that:
{player1Scored: 5, player2Scored: 2, player3Scored: 5, player4Scored:1}
I tried this:
let scoreResult = arr.reduce((result, {0: obj}) => {
obj.rivals.forEach(rival => result[`${rival}Scored`] = result[`${rival}Scored`] || 0);
obj.rivals.forEach(rival => result[`${rival}Scored`] += obj.player1Scored)
return result;
}, {});
My mistake is that I'm asigning points of one player to two of them but cannot solve that.
Thank you for your help
To answer the problem, you have nested arrays with a single object. You could take a destructuring with the array and take the firts item as object.
Then take the names from rivals array and add the scores.
const
array = [[{ rivals: ['player1', 'player2'], winner: "player1", player1Scored: 2, player2Scored: 1 }], [{ rivals: ['player1', 'player3'], winner: "none", player1Scored: 2, player3Scored: 2 }], [{ rivals: ['player2', 'player3'], winner: "player3", player2Scored: 1, player3Scored: 3 }], [{ rivals: ['player1', 'player4'], winner: "none", player1Scored: 1, player4Scored: 1 }]],
result = array.reduce((r, [o]) => {
o.rivals.forEach(k => r[k + 'Scored'] = (r[k + 'Scored'] || 0) + o[k + 'Scored']);
return r;
}, Object.create(null));
console.log(result);
try this
scores = {}
arr.map(s => s[0]).forEach(s => {
s.rivals.forEach(r => {
scores[r] = scores[r] || 0;
scores[r] += s[`${r}Scored`]
})
})