Get all unique combinations of a nested object - javascript

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.

Related

using map with filter array JavaScript

i have a json and need to extract data to array.
const data = [{
"week": 1,
"lost": 10,
"recovery_timespan": [{
"week": 2,
"count": 1
}, {
"week": 3,
"count": 0
}],
"netLost": 10,
"netReturned": 20
}, {
"week": 2,
"lost": 7,
"recovery_timespan": [{
"week": 3,
"count": 1
}],
"netLost": 30,
"netReturned": 200
}, {
"week": 3,
"lost": 8,
"recovery_timespan":"",
"netLost": 50,
"netReturned": 40
}];
Expected output: lost,count in recovery_timespan,netLost , netReturned.
[ [ 10, 1, 0, 10, 20 ], [ 7, 1, 30, 200 ], [ 8, 50, 40 ] ]
As you can see expected output, last recovery_timespan does not contain any data and it just shows as "".so i need to ignore it.
My approach:
const result = data.map(({lost, recovery_timespan,netLost,netReturned}) => [
lost,
...recovery_timespan.map(({count}) => count),
netLost,netReturned
]);
My code breaks when "recovery_timespan" is "". How can i add a filter along with map to filter that part and make my code work?
It's just a matter of checking if it's string or not, but you can short circuit
const result = data.map(({lost, recovery_timespan,netLost,netReturned}) => [
lost,
...(recovery_timespan || []).map(({count}) => count),
netLost,netReturned
]);

Count the Number of Elements with the same Value in a Row?

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)

Combine results from array of objects based on key in nested array of objects [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have array of objects where I want to filter and combine results based on specific id. This is example:
[
{
id: 1,
items: [
{
id: 10,
values: [11],
},
{
id: 20,
values: [13, 14, 15],
},
],
},
{
id: 2,
items: [
{
id: 10,
values: [12],
},
{
id: 20,
values: [13, 15],
},
],
},
];
And this is expected result:
[
{
id: 10,
values: [11, 12],
},
{
id: 20,
values: [13, 14, 15],
},
];
I also need to filter duplicates. Thanks
Note: What if I want this result?
[
{
// here I want value for id 10 (it will be always one number)
value: 11,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 14, 15],
},
{
// here I want value for id 10 (it will be always one number)
value: 12,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 15],
},
];
I tried the same approach with Map, but without success. Basically I want to combine values based on ids.
You could do with Array.flatMap to filter all items in single array.
Then recreate the array with Array.reduce and push the value based on id into new value object
And use Array.filter ignore the duplicate values on array
Object.values return only the value of object in array format
Older
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
const res = Object.values(arr.flatMap(({items})=> items)
.reduce((acc,{id,values})=>{
acc[id] = acc[id] ?? {id,values:[]};
//check the object exist or not
let newArr = acc[id]['values'].concat(values);
let valArr = newArr.filter((v,i)=>newArr.indexOf(v) === i)
//remove the duplicates
acc[id]['values'] = valArr
return acc
},{}))
console.log(res)
Updated
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
function filterMethod(arr,value,values){
return arr.map(({items})=> ({
value:detector(items,value)[0],
values:detector(items,values)
}))
}
function detector(items,idVal){
let ind = items.findIndex(({id})=> id === idVal);
return ind > -1 ? items[ind]['values'] : ['']
}
console.log(filterMethod(arr,10,20))

JS: Delete Object Key if all nested Values equal null

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

(Javascript) Need help converting an object to an array

I have an object which has objects inside in it at the following form:
Object {
"item1": { "subitem1": 5, "subitem2": 10 },
"item2": { "subitem1": 3, "subitem2": 12, "subitem3": 1 },
"item3": { "subitem1": 8, "subitem2": 1, "subitem3": 3 }
}
I want to convert it to an array with the following form:
[0] Object { key: "item1", "subitem1": 5, "subitem2": 10 }
[1] Object { key: "item2", "subitem1": 3, "subitem2": 12, "subitem3": 1 }
[2] Object { key: "item3", "subitem1": 8, "subitem2": 1, "subitem3": 3 }
Any ideas? Thanks in advance
You could get the entries of the object and assign the key.
var object = { item1: { subitem1: 5, subitem2: 10 }, item2: { subitem1: 3, subitem2: 12, subitem3: 1 }, item3: { subitem1: 8, subitem2: 1, subitem3: 3 } },
result = Object.entries(object).map(([key, o]) => Object.assign({ key }, o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Object.keys() and Array.prototype.map():
const obj = { "item1": { "subitem1": 5, "subitem2": 10 }, "item2": { "subitem1": 3, "subitem2": 12, "subitem3": 1 }, "item3": { "subitem1": 8, "subitem2": 1, "subitem3": 3 }},
res = Object.keys(obj).map(key => ({key, ...obj[key]}));
console.log(res);
Do note, however, the order of the keys retrieved from Object.keys is not guaranteed to be the same order as listed in your object
You can try this mate
let obj = { "item1": { "subitem1": 5, "subitem2": 10 }, "item2": { "subitem1": 3, "subitem2": 12, "subitem3": 1 }, "item3": { "subitem1": 8, "subitem2": 1, "subitem3": 3 } };
let op =[];
for(let key in obj){
op.push({
key : key,
...obj[key]
})
}
console.log(op);

Categories

Resources