Related
I have 3 two dimensional arrays as given below which are series data to plot lines on a graph with the key being the timestamp.
const arr1 = [[1641013200000,1881],[1643691600000,38993],[1646110800000,41337],[1648785600000,78856],[1651377600000,117738],[1654056000000,119869],[1656648000000,157799],[1659326400000,196752],[1662004800000,199061],[1664596800000,237034],[1667275200000,239153],[1669870800000,269967]]
const arr2 = [[1641013200000,1302],[1643691600000,3347],[1646110800000,4754],[1648785600000,6948],[1651377600000,9725],[1654056000000,11314],[1656648000000,13787],[1659326400000,16666],[1662004800000,18370],[1664596800000,20876],[1667275200000,22384],[1669870800000,23560]]
const arr3 = [[1643691600000,67350],[1648785600000,134700],[1651377600000,202148],[1654056000000,202270],[1656648000000,269843],[1659326400000,337346],[1662004800000,337470],[1664596800000,404861],[1667275200000,404889],[1669870800000,472239]]
I want to plot another series line which gives the cumulative total of all three arrays values
(Note: if a timestamp is not present in either of the arrays, add the previous index value)
const totalArray = [
[1641013200000,3183],[1643691600000, 109690],[1646110800000, 113441],[1648785600000, 220504],
[1651377600000, 329611],[1654056000000, 333453],[1656648000000, 441429],[1659326400000, 550764],
[1662004800000, 554901],[1664596800000, 662771],[1667275200000, 666426],[1669870800000, 765766]
]
I have tried this, but some values are incorrect due to the timestamp not being present in either one
Approach:
const arr1 = [
[1641013200000, 1881],
[1643691600000, 38993],
[1646110800000, 41337],
[1648785600000, 78856],
[1651377600000, 117738],
[1654056000000, 119869],
[1656648000000, 157799],
[1659326400000, 196752],
[1662004800000, 199061],
[1664596800000, 237034],
[1667275200000, 239153],
[1669870800000, 269967]
];
const arr2 = [
[1641013200000, 1302],
[1643691600000, 3347],
[1646110800000, 4754],
[1648785600000, 6948],
[1651377600000, 9725],
[1654056000000, 11314],
[1656648000000, 13787],
[1659326400000, 16666],
[1662004800000, 18370],
[1664596800000, 20876],
[1667275200000, 22384],
[1669870800000, 23560]
];
const arr3 = [
[1643691600000, 67350],
[1648785600000, 134700],
[1651377600000, 202148],
[1654056000000, 202270],
[1656648000000, 269843],
[1659326400000, 337346],
[1662004800000, 337470],
[1664596800000, 404861],
[1667275200000, 404889],
[1669870800000, 472239]
];
const calculateTotal = () => {
var ret;
for (let a3 of arr3) {
var index = arr1.map(function(el) {
return el[0];
}).indexOf(a3[0]);
console.log(index);
if (index === -1) {
ret = arr1[index][0];
console.log(ret);
}
}
let unsortedArr = arr1.concat(arr2, arr3);
var sortedArray = unsortedArr.sort((a, b) => a[0] - b[0]);
var added = addArray(sortedArray);
console.log("Curent Output: " + JSON.stringify(added));
}
const addArray = (tuples) => {
var hash = {},
keys = [];
tuples.forEach(function(tuple) {
var key = tuple[0],
value = tuple[1];
if (hash[key] === undefined) {
keys.push(key);
hash[key] = value;
} else {
hash[key] += value;
}
});
return keys.map(function(key) {
return ([key, hash[key]]);
});
}
calculateTotal();
Is it possible to achieve this?
In your code there is this:
if (index === -1) {
ret = arr1[index][0];
But that assignment will fail as arr1[-1] is not defined.
Then when you do:
let unsortedArr = arr1.concat(arr2, arr3);
...you end up with an array that does not have the knowledge to use default values (from a previous index) when any of the three arrays has a "missing" time stamp.
I would suggest this approach:
Collect all the unique timestamps (from all arrays) into a Map, and associate arrays to each of those keys: these will be empty initially.
Populate those arrays with the timestamps from the original arrays
Get the sorted list of entries from that map
Fill the "gaps" by carrying forward values to a next array when the corresponding slot is undefined. At the same time sum up these values for the final output.
Here is an implementation:
function mergeArrays(...arrays) {
const map = new Map(arrays.flatMap(arr => arr.map(([stamp]) => [stamp, []])));
arrays.forEach((arr, i) => {
for (const [timeStamp, value] of arr) {
map.get(timeStamp)[i] = value;
}
});
const state = Array(arrays.length).fill(0);
return Array.from(map).sort(([a], [b]) => a - b).map(([timeStamp, arr], i) =>
[timeStamp, state.reduce((sum, prev, j) => sum + (state[j] = arr[j] ?? prev), 0)]
);
}
// Example run
const arr1 = [[1641013200000,1881],[1643691600000,38993],[1646110800000,41337],[1648785600000,78856],[1651377600000,117738],[1654056000000,119869],[1656648000000,157799],[1659326400000,196752],[1662004800000,199061],[1664596800000,237034],[1667275200000,239153],[1669870800000,269967]];
const arr2 = [[1641013200000,1302],[1643691600000,3347],[1646110800000,4754],[1648785600000,6948],[1651377600000,9725],[1654056000000,11314],[1656648000000,13787],[1659326400000,16666],[1662004800000,18370],[1664596800000,20876],[1667275200000,22384],[1669870800000,23560]];
const arr3 = [[1643691600000,67350],[1648785600000,134700],[1651377600000,202148],[1654056000000,202270],[1656648000000,269843],[1659326400000,337346],[1662004800000,337470],[1664596800000,404861],[1667275200000,404889],[1669870800000,472239]];
const result = mergeArrays(arr1, arr2, arr3);
console.log(result);
I have a set of values (groups) in a comma delimited string and would like to check if any of those values match any array items (reqRights) and return true or false, but is returns undefined error.
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
function checker(value) {
var groups = groups.split(",");
console.log(groups);
return groups.every(function(v) {
return value.indexOf(v) !== -1;
});
}
arr = reqRights.filter(checker);
console.log(arr);
js engine SpiderMonkey 1.8, does not support .includes and some methods
You're using const on groups, so you cannot reassign it. And you also should move groups.split(",") outside of checker function to avoid creating a new array every time calling checker.
some can help you to check regRights item is in groups instead of every.
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
const groupsArray = groups.split(",");
function checkGroupValue(group) {
return group === value;
}
function checker(value) {
return groupsArray.some(checkGroupValue);
}
arr = reqRights.filter(checker);
console.log(arr);
const reqRights = ["18900253","3217840","1053"];
const groupsConstant = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
function checker(value) {
var groups = groupsConstant.split(",");
return groups.every(v => groups.indexOf(v) !== -1);
}
arr = reqRights.filter(checker);
console.log('elements matched: ', arr.length ? 'yes' : 'no');
I was trying to avoid the indexOf as it might give a false positive result (example: reqRight "1053" would be listed if found in "1053568")
THis is not necessarily the shortest way, but should be compatible with spiderMonkey 1.8
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
function checker(values, required) {
var valuesArray = values.split(",");
return valuesArray.filter((value) => {
return required.filter((req) => {
return req === value;
}).length > 0;
})
}
arr = checker(groups, reqRights);
console.log(arr);
Use Array.find in filtering from groups:
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
const checker = value =>
({[value]: reqRights.find(v => value === v) ? true : false});
const checked = groups.split(`,`).map(checker);
console.log(checked);
Or maybe concat both (groups as array) arrays, sort that numerically and filter the result on double values.
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
const checked = groups.split(`,`)
.concat(reqRights)
.sort( (a, b) => +a - +b)
.filter((v, i, self) => self[i+1] === v);
console.log(checked);
You can simply achieve this by using Array.map() along with Array.some() method.
Live Demo :
const reqRights = ["18900253","3217840","1053"];
const groups = "3217635,18272308,1053,3217633,18900253,3217698,3217699,3217840,10162510";
const res = groups.split(',').map(item => {
return reqRights.some(n => item === n)
});
console.log(res);
I have an array of strings e.g. const array1 = ["124542", "112244", "112245", "112246"];
I want to create below results:
const array2 = [
{
"comparableId": "124542",
"comparatorId": "112244"
},
{
"comparableId": "112244",
"comparatorId": "112245"
},
{
"comparableId": "112245",
"comparatorId": "112246"
}
]
In this array2, 1st index value from array1 is with 2nd value, 2nd value with 3rd and 3rd with 4th and so on. This array1 is dynamic and can have just 1 value e.g. const array1 = ["124542"]; and can have any number of values.
I have tried using Ecma Script reduce and map methods.
const array2 = array1.map((value, index) => {
return {
comparableId: value,
comparatorId: array1[index + 1] || '',
};
});
Is there some other good way to do it ?
Using a for loop is probably the easiest here.
const array1 = ["124542", "112244", "112245", "112246"];
const transform = (array) => {
const result = [];
for (let i = 0; i === 0 || i < array.length - 1; ++i) {
result.push({
comparableId: array[i],
comparatorId: array[i + 1] ?? "",
})
}
return result;
};
console.log(transform(array1));
console.log(transform(["124542"]));
Fully declarative, no mutations solution.
const
array1 = ["124542", "112244", "112245", "112246"],
array2 = [...Array(Math.ceil(array1.length / 2))].map((_, i) =>
({comparableId: array1[i * 2], comparatorId: array1[i * 2 + 1] ?? ""})
);
console.log(array2);
const array1 = ["124542", "112244", "112245", "112246"];
let array2 = [];
array1.map((item, index) =>{
array2.push({comparableId: item, comparatorId: array1[index+1] || ""})
})
console.log(array2)
A loop is the best option.
const array1 = ["124542", "112244", "112245", "112246"]
let array2 = [];
for (let i = 0; i < array1.length; i + 2) {
array2[i] = {
"comparableId": array2[i],
"comparatorId": array2[i + 1]
}
console.log(array2[i])
}
array2 won't be a const variable as it is going to be modified each loop. If you really need it to be a constant then you'll have to define a new constant like so: const array3 = array2; at the end of your code.
I'm trying to find the opposite number in the array.
Let's say we have this array: [1,3,4,-3]
What I want is:
{ '1': 1, '3': 2, '4': 1 } // as we have two 3
and what I tried is :
const arr = [1,3,4,-3]
let holder = {}
arr.map(a => {
holder[a] = (holder[a] ||0)+1
})
console.log(holder)
Any idea how can I acheive this ?
The rules can be summed up as follow:
Let v be the array
Let x a positive number in the array
if x is in v but not -x, keep {[x]:1}
if x is in v and -x also, keep {[x]:2}
Let y a negative number in the array
if y is in v and -y also do nothing (symetry of 2.)
if y is in v but not -y, keep {[y]:1}
Now it is pretty straightforward:
build a set of values of v
check all the positive keys
foreach one, check if there exists its opposite and apply 1. or 2.
delete any opposite keys
for the remaining keys (which are then negative and have no opposite) apply 3.
const v = [1,1,1,-2, 3,3,-3, 4,-4,-4]
const output = {}
const s = new Set(v)
const positives = [...new Set(v.filter(x => x>= 0))]
positives.forEach(p => {
if (s.has(-p)) {
output[p] = 2
s.delete(-p)
} else {
output[p] = 1
}
s.delete(p)
})
;[...s].forEach(negative => {
output[negative] = 1
})
console.log('output', output)
Now we can be a bit more aggressive regarding the symetry:
if x === -y
upon iterating x, setting {keep[x]: 2}
then upon iterating -y, setting {keep[-y]: 2} is equivalent to setting {keep[x]: 2} which has already been done.
So we may or may not set {keep[-y]: 2}, this is equivalent
const v = [1,1,1,-2, 3,3,-3, 4,-4,-4]
const output = {}
const s = new Set(v)
;[...s].forEach(x => {
if (s.has(-x)) {
output[Math.abs(x)] = 2
} else {
output[x] = 1
}
})
console.log('output', output)
Not quite sure if this is really what you trying to achieve. If so, you can do it with a reduce. Please check:
const arr = [1, 3, 4, -3];
const obj = arr.reduce((acc, cur) => ({
...acc,
[Math.abs(cur)]: (acc[Math.abs(cur)] || 0) + 1,
}), {});
console.log(obj);
const arr = [1,3,4,-3];
const allPositive = arr.map(item => Math.abs(item));
const reducer = (acc, item, index) => {
const count = allPositive.filter(x => x===item).length;
acc[item] = count;
return acc
};
const result = allPositive.reduce(reducer, {});
console.log(result)
Looking to remove an array item from the nested array if subset array have value a null or 0(zero) using lodash. I tried filter but I am looking for best way in term of performance.
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
console.log("arr", arr);
// output should be [["a","b","c"],["d","e","f"]]
You can use filter() and some()
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
let res = arr.filter(x => !x.some(a => a === null || a === 0))
console.log(res)
With lodash : filter and includes functions:
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
const result = _.filter(arr, x => !_.includes(x, null) && !_.includes(x, 0))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.14/lodash.min.js"></script>
With ES6 : filter and some functions:
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
const result = arr.filter( x => !x.some(s => s === null || s === 0))
console.log(result)
With ES6 : reduce:
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
const result = arr.reduce( (acc, c) => {
if(c.some(x => x === null || x === 0)) {
return acc
}
return [...acc, c]
},[])
console.log(result)
//cost will not let modify the variable
let arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"]];
arr = arr.filter(aItem=>{
//compact will remove null, 0 , undefined values from array item
return aItem.length===_.compact(aItem).length;
});
console.log("...arr",arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.14/lodash.core.min.js"></script>
You can try with filter() and some().
Please Note: The following solution will also work for other falsy inputs like "" and undefined.
Using Lodash:
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"],["d",undefined,"f"],["d","e","f",""]];
var res = _.filter(arr, a => !a.some(i => !i));
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.14/lodash.core.min.js" integrity="sha256-NAQPwApfC7Ky1Y54LjXf7UrUQFbkmBEPFh/7F7Zbew4=" crossorigin="anonymous"></script>
Using Vanilla JS:
const arr = [["a","b","c"],["f","r","p",0],["r",22,null,"t"],["d","e","f"],["d",undefined,"f"],["d","e","f",""]];
var res = arr.filter(a => !a.some(i => !i));
console.log(res);