Function on a nested array in js - javascript

Here is my Function which turns timestamps to seconds,
const convertor = (x) => {
const result = x.split(':');
const final =
parseInt(result[0] * 60) +
parseInt(result[1] * 60) +
parseInt(result[2]) -
1.2;
return final;
};
input = 00:01:02.330
result: 62.330
How can I implement it on a nested array like this?
array = [ [ 00:01:02.330],[00:01:04.550],[01:11:02.330] ], [ [00:01:02.330],[00:01:02.330] ] , [ [00:01:02.330],[00:01:02.330],[00:01:02.330] ] ]
I want to preserve its nested structure so the result should be:
[ [ 22.3],[28.5],[903.50] ], [ [1252.3],[62.2] ] , [ [654.25],[965.25],[1254.32] ] ]

You can write a wrapper that accepts a function and an array, and maps over the array-- if the item is an array, it recurses; otherwise, it passes the item into the passed function-- this allows you to process nested arrays of any arbitrary depth and retain the desired structure:
const convertor = (x) => {
const result = x.split(':');
const final =
parseInt(result[0] * 60) +
parseInt(result[1] * 60) +
parseInt(result[2]) -
1.2;
return final;
};
const recursiveNestedMapper = (fn, arr) => {
const output = arr.map((item) => {
if (Array.isArray(item)) {
return recursiveNestedMapper(fn, item);
} else {
return fn(item);
}
});
return output;
}
const inputArray = [
[
['00:01:02.330'],
['00:01:04.550'],
['01:11:02.330']
],
[
['00:01:02.330'],
['00:01:02.330']
],
[
['00:01:02.330'],
['00:01:02.330'],
['00:01:02.330']
]
];
const outArray = recursiveNestedMapper(convertor, inputArray);
console.log(outArray);

Not sure why you're keeping the timestamps in an array when it's the only element, but you can get the desired output by chaining map()
const convertor = (x) => {
const result = x[0].split(':');
const final =
parseInt(result[0] * 60) +
parseInt(result[1] * 60) +
parseInt(result[2]) -
1.2;
return final;
};
const array = [
[ [ '00:01:02.330' ], [ '00:01:04.550' ], [ '01:11:02.330' ] ],
[ [ '00:01:02.330' ], [ '00:01:02.330' ] ],
[ [ '00:01:02.330' ], [ '00:01:02.330' ], [ '00:01:02.330' ] ]
];
const output = array.map(a => a.map(b => convertor(b)));
console.log(output);
[
[
60.8,
62.8,
720.8
],
[
60.8,
60.8
],
[
60.8,
60.8,
60.8
]
]

Related

How to include zero counts in a javascript frequency counting function for histograms

Need some help please - am using the excellent response to this histogram bucketing question as per code below. My question is what is the most elegant way to get my buckets with zero counts represented in the function output?
Current Output is:
[ [ '0', 2 ],
[ '32', 1 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
which omits to note buckets 16 and 48 which have zero counts.
My desired output is:
[ [ '0', 2 ],
[ '16', 0 ],
[ '32', 1 ],
[ '48', 0 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
All help much appreciated.
function defArrs (){
var arr = [1,16,38,65,78,94,105,124]
var binsize = 16;
var check = Object.entries(frequencies (arr,binsize));
console.log(check);
}
function frequencies(values, binsize) {
var mapped = values.map(function(val) {
return Math.ceil(val / binsize) -1;
});
console.log(mapped);
return mapped.reduce(function (freqs, val, i) {
var bin = (binsize * val);
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
return freqs;
}, {});
}
Instead of starting with the empty object for .reduce:
}, {});
// ^^
construct one with all the properties you'll want to be included at the end (starting with a count of 0, of course).
const initialObj = Object.fromEntries(
Array.from(
{ length: 7 },
(_, i) => [i * binsize, 0]
)
);
And pass that as the second parameter to .reduce.
That approach also means that this
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
will simplify to
freqs[bin]++;
Or, even better:
const frequencies = (nums, binsize) => {
const mapped = nums.map(num => Math.ceil(num / binsize) - 1;
const grouped = Object.fromEntries(
{ length: 7 },
(_, i) => [i * binsize, 0]
);
for (const val of mapped) {
grouped[binsize * val]++;
}
return grouped;
};

Split array elements dynamically

I have below scenarios to handle.
let data = [
[ "ALISHA", "SUICA", "PASMO" ],
[ "HARMONY" ],
[ "OCTOPUS" ]
]
let data1 = [
[ "ALISHA", ],
[ "HARMONY" ],
[ "OCTOPUS", "SUICA", "PASMO" ]
]
For both of the above data, i want the result to look like this.
let result = [
[ "ALISHA" ],
[ "HARMONY" ],
[ "OCTOPUS" ],
[ "SUICA" ],
[ "PASMO" ]
]
Can someone please let me know how to achieve this. I tried the following but no success
let result = []
for (let i = 0; i < data.length; i++) {
let split = data[i].split(","); // just split once
result.push(split[0]); // before the comma
}
we will use forEach method on main array inside forEach we will use if condition if is array and length more than 1 will add another forEach method and push sub array to main array after that remove sub array
look like that
let data = [
["ALISHA"],
["HARMONY"],
["OCTOPUS", "SUICA", "PASMO"]
]
data.forEach((cur, index) => {
if (Array.isArray(cur) && cur.length > 1) {
cur.forEach(cur => data.push([cur]))
data.splice(index, 1);
}
})
console.log(data)
Uses Array.reduce extract all elements, then convert to [string] by Array.map.
const data = [
[ "ALISHA" ],
[ "HARMONY" ],
[ "OCTOPUS", "SUICA", "PASMO" ]
]
console.log(
data.reduce((pre, cur) => [...pre, ...cur], []).map(item => [item])
// data.reduce((pre, cur) => pre.concat(...cur), []).map(item => [item]) // different methods but same logic (uses Array.concat instead of spread operator)
)
You can use flat and map
const data = [["ALISHA"], ["HARMONY"], ["OCTOPUS", "SUICA", "PASMO"]];
const result = data.flat().map((a) => [a]);
console.log(result);

Javascript: update a column in matrix

I want to update an entire column of a matrix.
I started by initializing the matrix like:
m =({length:10}, (_,i) => Array.from({length:2}, (_,j) => i+'x'+j));
Then, I have an array of 2 element (say, a2) that I want to put in the j-th column of the matrix with manipulation.
If I am using matlab I would do the following: m(:,j)=a2+ m(:,j)
How can I do this in javascript?
Do you mean like this?:
const m = Array.from({length:10}, (_,i) => Array.from({length:2}, (_,j) => i+'x'+j));
console.log("before", m);
const j = 2;
const a2 = "z"
const n = m.map(row => {row.splice(j, 0, a2); return row});
console.log("after", n);
before manipulation:
before [
[ "0x0", "0x1" ],
[ "1x0", "1x1" ],
[ "2x0", "2x1" ],
[ "3x0", "3x1" ],
[ "4x0", "4x1" ],
[ "5x0", "5x1" ],
[ "6x0", "6x1" ],
[ "7x0", "7x1" ],
[ "8x0", "8x1" ],
[ "9x0", "9x1" ]
]
And after manipulation (i.e. add "z" to column 2):
after [
[ "0x0", "0x1", "z" ],
[ "1x0", "1x1", "z" ],
[ "2x0", "2x1", "z" ],
[ "3x0", "3x1", "z" ],
[ "4x0", "4x1", "z" ],
[ "5x0", "5x1", "z" ],
[ "6x0", "6x1", "z" ],
[ "7x0", "7x1", "z" ],
[ "8x0", "8x1", "z" ],
[ "9x0", "9x1", "z" ]
]
Or if a2 should be an array itself? Then, the simple case that it is a 1D array:
const m = Array.from({length:10}, (_,i) => Array.from({length:2}, (_,j) => i+'x'+j));
console.log("before", m);
const j = 2;
const a2 = Array.from({length:10}, (_,j) => 'z'+j)
const n = m.map((row, i) => {row.splice(j, 0, a2[i]); return row});
console.log("after", n);
After manipulation (i.e. add "z{ i }" to column 2):
after [
[ "0x0", "0x1", "z0" ],
[ "1x0", "1x1", "z1" ],
[ "2x0", "2x1", "z2" ],
[ "3x0", "3x1", "z3" ],
[ "4x0", "4x1", "z4" ],
[ "5x0", "5x1", "z5" ],
[ "6x0", "6x1", "z6" ],
[ "7x0", "7x1", "z7" ],
[ "8x0", "8x1", "z8" ],
[ "9x0", "9x1", "z9" ]
]
Or the more complex case when a2 is a 2D array:
const m = Array.from({length:10}, (_,i) => Array.from({length:2}, (_,j) => i+'x'+j));
console.log("before", m);
const j = 2;
const a2 = Array.from({length:10}, (_,i) => Array.from({length:2}, (_,j) => i+'z'+j));
const n = m.map((row, i) => {row.splice(j, 0, a2[i]); return row.flat()});
console.log("after", n);
After manipulation (i.e. add "z{ i }" to column 2):
after [
[ "0x0", "0x1", "0z0", "0z1" ],
[ "1x0", "1x1", "1z0", "1z1" ],
[ "2x0", "2x1", "2z0", "2z1" ],
[ "3x0", "3x1", "3z0", "3z1" ],
[ "4x0", "4x1", "4z0", "4z1" ],
[ "5x0", "5x1", "5z0", "5z1" ],
[ "6x0", "6x1", "6z0", "6z1" ],
[ "7x0", "7x1", "7z0", "7z1" ],
[ "8x0", "8x1", "8z0", "8z1" ],
[ "9x0", "9x1", "9z0", "9z1" ]
]
I think using Array.prototype.forEach and Array.prototype.splice is a good way to accomplish this:
// Setup for demo
const Row = (w) =>Array.from({length:w}).fill('').map((c,i)=>c=i);
const Matrix = (h, w) => Array.from({length:h}).fill('').map(r=>r=Row(w));
const print = (mtx) => mtx.forEach(r=>console.log([...r].join(", ")));
// Original state of matrix
const m1 = Matrix(2,10);
print(m1);
console.log('----------');
// Insertion function
const insertCol = (mtx, cInd, newCol) => {
mtx.forEach( (row, rInd) => { row.splice(cInd, 0, newCol[rInd]); }
);
}
// Matrix with inserted column
const someCol = ['X','Y'];
insertCol(m1, 3, someCol);
print(m1);

Separate string by numbers and characters

I am trying to format a string to produce a new string in the correct format:
I have the following strings (left) which should be formatted to match (right):
[ 'xx9999', 'XX-99-99' ],
[ '9999xx', '99-99-XX' ],
[ '99xx99', '99-XX-99' ],
[ 'xx99xx', 'XX-99-XX' ],
[ 'xxxx99', 'XX-XX-99' ],
[ '99xxxx', '99-XX-XX' ],
[ '99xxx9', '99-XXX-9' ],
[ '9xxx99', '9-XXX-99' ],
[ 'xx999x', 'XX-999-X' ],
[ 'x999xx', 'X-999-XX' ],
[ 'xxx99x', 'XXX-99-X' ],
[ 'x99xxx', 'X-99-XXX' ],
[ '9xx999', '9-XX-999' ],
[ '999xx9', '999-XX-9' ]
I have tried the following but cannot get it to work correctly:
const formatLp = (userInput) => {
if (userInput) {
return userInput.toUpperCase().match(/[a-z]+|[^a-z]+/gi).join('-');
}
}
This works for some of them, such as 99xxx9 but not others such as xx9999
any help would be appreciated.
Use .replace twice - once to insert a - between 4 repeated digits/non-digits, and once to insert a - between digits and alphabetical characters:
const arr = [
[ 'xx9999', 'XX-99-99' ],
[ '9999xx', '99-99-XX' ],
[ '99xx99', '99-XX-99' ],
[ 'xx99xx', 'XX-99-XX' ],
[ 'xxxx99', 'XX-XX-99' ],
[ '99xxxx', '99-XX-XX' ],
[ '99xxx9', '99-XXX-9' ],
[ '9xxx99', '9-XXX-99' ],
[ 'xx999x', 'XX-999-X' ],
[ 'x999xx', 'X-999-XX' ],
[ 'xxx99x', 'XXX-99-X' ],
[ 'x99xxx', 'X-99-XXX' ],
[ '9xx999', '9-XX-999' ],
[ '999xx9', '999-XX-9' ]
];
arr.forEach(([str]) => {
const result = str.toUpperCase()
.replace(/\d{4}|\D{4}/, substr => `${substr.slice(0, 2)}-${substr.slice(2)}`)
.replace(/[a-z]{4}|\d(?=[a-z])|[a-z](?=\d)/gi, '$&-');
console.log(result);
});
You can also do it by matching and then joining - match 3 non-digits, or 3 digits, or 1-2 non-digits, or 1-2 digits:
const arr = [
[ 'xx9999', 'XX-99-99' ],
[ '9999xx', '99-99-XX' ],
[ '99xx99', '99-XX-99' ],
[ 'xx99xx', 'XX-99-XX' ],
[ 'xxxx99', 'XX-XX-99' ],
[ '99xxxx', '99-XX-XX' ],
[ '99xxx9', '99-XXX-9' ],
[ '9xxx99', '9-XXX-99' ],
[ 'xx999x', 'XX-999-X' ],
[ 'x999xx', 'X-999-XX' ],
[ 'xxx99x', 'XXX-99-X' ],
[ 'x99xxx', 'X-99-XXX' ],
[ '9xx999', '9-XX-999' ],
[ '999xx9', '999-XX-9' ]
];
arr.forEach(([str]) => {
const result = str.toUpperCase()
.match(/[a-z]{3}|\d{3}|[a-z]{1,2}|\d{1,2}/gi)
.join('-');
console.log(result);
});
I've just implemented this using Stack. Here is the function. You just need to pass the string to this function.
const convert = (str) => {
let stack = str.split('').reduce((newArr, char, index) => {
if(index !== 0 && newArr[newArr.length-1] !== char) {
newArr.push('-');
newArr.push(char);
return newArr;
} else {
newArr.push(char);
return newArr;
}
},[])
return stack.join('').toUpperCase();
}
// Here you can check this in action
const convert = (str) => {
let stack = str.split('').reduce((newArr, char, index) => {
if(index !== 0 && newArr[newArr.length-1] !== char) {
newArr.push('-');
newArr.push(char);
return newArr;
} else {
newArr.push(char);
return newArr;
}
},[])
return stack.join('').toUpperCase();
}
const strings = [ 'xx9999','9999xx','99xx99','xx99xx','xxxx99','99xxxx','99xxx9','9xxx99','xx999x','x999xx','xxx99x','x99xxx','9xx999','999xx9',];
strings.map(string => console.log(convert(string)))

How can I combine elements of an array that are linear?

My data looks like:
[ [ '0s', '0.200s' ],
[ '0.200s', '0.600s' ],
[ '1.600s', '2.500s' ],
[ '3.500s', '3.900s' ],
[ '3.900s', '4.400s' ],
[ '4.400s', '4.600s' ],
[ '4.600s', '4.700s' ],
[ '4.700s', '5.200s' ],
[ '5.200s', '5.400s' ],
[ '5.400s', '5.800s' ],
[ '5.800s', '6.100s' ],
[ '6.100s', '6.800s' ],
[ '6.800s', '7s' ],
[ '7s', '7.300s' ],
[ '7.300s', '7.500s' ]
]
The first element ends at 0.200s which is where the second element begins. So I want those 2 to combine to be ['0s', '0.600s'].
The next element doesn't start where this one ends, so it should continue on. Ultimately, the result should look like:
[ [ '0s', '0.600s' ],
[ '1.600s', '2.500s' ],
[ '3.500s', '7.500s' ]
]
I am trying to do it recursively, but it's giving errors. Here's my function:
function combineStartsEnds(timecodes) {
if (timecodes[0][1] === timecodes[1][0]) {
let combined = [
[timecodes[0][0], timecodes[1][1]]
].concat(_.slice(timecodes, 2));
return combineStartsEnds(combined);
} else {
return timecodes[0].concat(combineStartsEnds(_.slice(timecodes, 1)));
}
};
This gives an error:
TypeError: Cannot read property '0' of undefined
Any ideas on how to accomplish this?
You are missing brackets here, this:
return timecodes[0].concat(...)
must be:
return [timecodes[0]].concat(...)
Additionally you need a base case to end the recursion:
function combineStartsEnds(timecodes) {
if(!timecodes.length) return [];
How I would do that:
function combineStartsEnds(timecodes) {
const result = []; let previous = [];
for(const [start, end] of timecodes) {
if(start === previous[/*end*/ 1]) {
previous[/*end*/ 1] = end;
} else {
result.push(previous = [start, end]);
}
}
return result;
}
You can also do this with reduce.
const times = [ [ '0s', '0.200s' ],
[ '0.200s', '0.600s' ],
[ '1.600s', '2.500s' ],
[ '3.500s', '3.900s' ],
[ '3.900s', '4.400s' ],
[ '4.400s', '4.600s' ],
[ '4.600s', '4.700s' ],
[ '4.700s', '5.200s' ],
[ '5.200s', '5.400s' ],
[ '5.400s', '5.800s' ],
[ '5.800s', '6.100s' ],
[ '6.100s', '6.800s' ],
[ '6.800s', '7s' ],
[ '7s', '7.300s' ],
[ '7.300s', '7.500s' ]
];
const merged = times.reduce((acc, [t3, t4]) => {
const [t1, t2] = acc[acc.length - 1] || [null, null];
if (t2 === t3) {
acc.pop();
acc.push([t1, t4]);
} else {
acc.push([t3, t4]);
}
return acc;
}, []);
console.log(merged);
You can also try below method to get your desired result.
(1) Flatten the array, you will get
arr.flat()
["0s", "0.200s", "0.200s", "0.600s", "1.600s", "2.500s", "3.500s", "3.900s", "3.900s", "4.400s", "4.400s", "4.600s", "4.600s", "4.700s", "4.700s", "5.200s", "5.200s", "5.400s", "5.400s", "5.800s", "5.800s", "6.100s", "6.100s", "6.800s", "6.800s", "7s", "7s", "7.300s", "7.300s", "7.500s"]
(2) Filter and remove elements if same exists before and after it's position, you will get
arr.flat().filter((d,i,c) => d != c[i-1] && d != c[i+1])
["0s", "0.600s", "1.600s", "2.500s", "3.500s", "7.500s"]
(3) Reduce the above result to the format you need
arr.flat()
.filter((d,i,c) => d != c[i-1] && d != c[i+1])
.reduce((res, d, i, c) => (i%2 == 0 && res.push([d, c[i+1]]) , res) , [])
[["0s", "0.600s"]
["1.600s", "2.500s"]
["3.500s", "7.500s"]]
How about using a Map():
const data = [
['0s', '0.200s'],
['0.200s', '0.600s'],
['1.600s', '2.500s'],
['3.500s', '3.900s'],
['3.900s', '4.400s'],
['4.400s', '4.600s'],
['4.600s', '4.700s'],
['4.700s', '5.200s'],
['5.200s', '5.400s'],
['5.400s', '5.800s'],
['5.800s', '6.100s'],
['6.100s', '6.800s'],
['6.800s', '7s'],
['7s', '7.300s'],
['7.300s', '7.500s']
];
var data_map = new Map(data);
for (var [key, value] of data_map) {
while (data_map.has(value)) {
var new_value = data_map.get(value);
data_map.set(key, new_value);
data_map.delete(value);
value = new_value;
}
}
data_map.forEach((value, key) => console.log(`[${key}, ${value}]`));
You could reduce the array with a single loop by checking the values and update either the last array or push a new array to the result set.
var data = [['0s', '0.200s'], ['0.200s', '0.600s'], ['1.600s', '2.500s'], ['3.500s', '3.900s'], ['3.900s', '4.400s'], ['4.400s', '4.600s'], ['4.600s', '4.700s'], ['4.700s', '5.200s'], ['5.200s', '5.400s'], ['5.400s', '5.800s'], ['5.800s', '6.100s'], ['6.100s', '6.800s'], ['6.800s', '7s'], ['7s', '7.300s'], ['7.300s', '7.500s']],
combined = data.reduce((r, [a, b]) => {
var last = r[r.length - 1];
if (last && a === last[1]) {
last[1] = b;
} else {
r.push([a, b]);
}
return r;
}, []);
console.log(combined);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One way to do it using recursion –
const start = ([ a, b ]) =>
a
const end = ([ a, b ]) =>
b
const join = ([ a, b, ...rest ]) =>
// base: no `a`
a === undefined
? []
// inductive: some `a`
: b === undefined
? [ a ]
// inductive: some `a` and some `b` (joinable)
: end (a) === start (b)
? join ([ [ start (a), end (b) ], ...rest ])
// inductive: some `a` and some `b` (non-joinable)
: [ a, ...join ([ b, ...rest ]) ]
const data =
[ [ '0s', '0.200s' ]
, [ '0.200s', '0.600s' ]
, [ '1.600s', '2.500s' ]
, [ '3.500s', '3.900s' ]
, [ '3.900s', '4.400s' ]
, [ '4.400s', '4.600s' ]
, [ '4.600s', '4.700s' ]
, [ '4.700s', '5.200s' ]
, [ '5.200s', '5.400s' ]
, [ '5.400s', '5.800s' ]
, [ '5.800s', '6.100s' ]
, [ '6.100s', '6.800s' ]
, [ '6.800s', '7s' ]
, [ '7s', '7.300s' ]
, [ '7.300s', '7.500s' ]
]
console.log (join (data))
// [ [ '0s', '0.600s' ]
// , [ '1.600s', '2.500s' ]
// , [ '3.500s', '7.500s' ]
// ]

Categories

Resources