Related
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;
};
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);
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);
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)))
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' ]
// ]