JavaScript array views - javascript

Via XMLHttpRequest I have received arraybuffer of Uint32 values
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response;
if (arrayBuffer) {
pointsArray = new Uint32Array(arrayBuffer);
However, I know that this array has an internal structure.
Say, pointsArray length is 10 but I know it contains 5 points X,Y coordinates.
How can I create( hopefully without copy ) two new 'views' at this pointsArray so that I can
index X and Y points separately?
Something like:
var xArray = something (pointsArray)
var yArray = something else (pointsArray)
Then, even if pointsArray length is 10, my new two arrays will have a length of 5 so I can index them from 0 to 4.
EDIT:
The question is imprecise. It implies that original input array can't be modified so the answer by David Alvarez is correct, despite the stated preference for avoiding any copies, which, in turn, for the best performance may require the format of the input array to be modified.

If there are 5 consecutive X coordinates and then 5 consecutive Y coordinates, you can create arrays on the same buffer:
let xArray = new Uint32Array(arrayBuffer,0,5);
let yArray = new Uint32Array(arrayBuffer,5*4,5);
(where 4 could be Uint32Array.BYTES_PER_ELEMENT)
But otherwise you will have to copy elements around, at least inside the array.
Side note: TypedArrays use platform-native byte order, so generally you can not avoid dealing with all data elements, at least in a conditional branch swapping bytes if necessary, or you can use DataView.getUint32(bytOffset,littleEndian) and you are back on the starting field, accessing elements individually.

What you can do is create functions.
const xArray = () => pointsArray.filter((element, index) => index % 2 === 0)
const yArray = () => pointsArray.filter((element, index) => index % 2 !== 0)
In that manner, no values will be copied or processed until you call xArray() or yArray(). That is what I think is the closest to a "view".
EDIT
If you want to use that "view" to directly access nth element:
const nthX = (n) => pointsArray.filter((element, index) => index % 2 === 0)[n]
const nthY = (n) => pointsArray.filter((element, index) => index % 2 !== 0)[n]
Then call nthX(2) if you want the x at the index 2 (of the array contianing all the x's)
EDIT 2
If you want the same behavior without copying the array you can do:
const nthX = (n) => pointsArray[n*2]
const nthY = (n) => pointsArray[n*2+1]

Related

how to get the last 3 elements with specific name value with JMESPATH and javascript?

I have the below json data array:
[{"name":"Button1","date":"1596959802144"},{"name":"Button2","date":"1596959804238"},{"name":"Button3","date":"1596959809334"},{"name":"Button1","date":"1597000878135"},{"name":"Button2","date":"1597000896335"},{"name":"Button3","date":"1597000901536"},{"name":"Button2","date":"1597000904437"},{"name":"Button3","date":"1597000909535"},{"name":"Button1","date":"1597000912250"},{"name":"Button2","date":"1597000939937"},{"name":"Button3","date":"1597000957940"},{"name":"Button2","date":"1597000964640"},{"name":"Button1","date":"1597001005141"},{"name":"Button2","date":"1597001010240"},{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button1","date":"1597001025738"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button1","date":"1597001057741"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002599045"},{"name":"Button1","date":"1597002604128"},{"name":"Button1","date":"1597002609546"},{"name":"Button1","date":"1597002613435"},{"name":"Button1","date":"1597002681736"},{"name":"Button1","date":"1597002690843"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}]
and I would like to use JMESPath javascript library to get only the last 3 entries per each distinct name value. For example:
[{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}]
So the last 3 occurrences fro each name = Button*
checking on stackOverflow and I saw that with JQ is possible to do using this function: map_values(delpaths(keys_unsorted[:-2] | map([.])))
Get last N elements for each item of a JSON object
Is there any way to do? or using other javascript module?
If you don't care about the order in your resulting array, here would be a pure JavaScript way to do this:
const getLastNForEveryName = (arr, n) => {
const lastNOfEach = arr.reduce((acc, curr) => {
if(acc[curr.name] == null) { // If the key doesnt exist yet, create it with the current item in the array
acc[curr.name] = [curr];
} else {
if(acc[curr.name].length >= n) // If the array is as big as the desired size alread, remove the first added one
acc[curr.name].shift();
acc[curr.name].push(curr); // push the current item in the array
}
return acc;
}, {})
return Object.values(lastNOfEach).flatMap(l => l); // Just get the values of the object and flatMap it, so that we dont have arrays of arrays
}
// Testing
const values = [{"name":"Button1","date":"1596959802144"},{"name":"Button2","date":"1596959804238"},{"name":"Button3","date":"1596959809334"},{"name":"Button1","date":"1597000878135"},{"name":"Button2","date":"1597000896335"},{"name":"Button3","date":"1597000901536"},{"name":"Button2","date":"1597000904437"},{"name":"Button3","date":"1597000909535"},{"name":"Button1","date":"1597000912250"},{"name":"Button2","date":"1597000939937"},{"name":"Button3","date":"1597000957940"},{"name":"Button2","date":"1597000964640"},{"name":"Button1","date":"1597001005141"},{"name":"Button2","date":"1597001010240"},{"name":"Button3","date":"1597001014845"},{"name":"Button2","date":"1597001021644"},{"name":"Button1","date":"1597001025738"},{"name":"Button2","date":"1597001049030"},{"name":"Button3","date":"1597001054139"},{"name":"Button1","date":"1597001057741"},{"name":"Button2","date":"1597001060340"},{"name":"Button3","date":"1597001062445"},{"name":"Button1","date":"1597002599045"},{"name":"Button1","date":"1597002604128"},{"name":"Button1","date":"1597002609546"},{"name":"Button1","date":"1597002613435"},{"name":"Button1","date":"1597002681736"},{"name":"Button1","date":"1597002690843"},{"name":"Button1","date":"1597002694136"},{"name":"Button1","date":"1597002696349"},{"name":"Button1","date":"1597002699243"}];
console.log(getLastNForEveryName(values, 3));

Remove by index value from props array

I am trying to remove a value by Index of the props array passed from another component.
[...this.props.data].splice([...this.props.data].indexOf(oldData), 1)
const {tableData, ...application} = oldData;
this.props.deleteData(application);
It deletes the data, but not just the selected value, but both values at the same time. I guess the problem is in the splice..indexOf
oldData :is the selected row that needs to be deleted.
You need to concat from 0 to index - 1 and from index + 1 to length - 1. So a simple this.props.data.slice(0, index).concat(this.props.data.slice(index) + 1) Should work.
Imo concat is easier to read and reason about because it does not mutate your array.
A filter could also work for you:
const filterIndex = target => (_, i) => i !== target;
newData = data.filter(filterIndex(index));
To use the filter version is pretty easy, two ways, depending on the use case.
1) Remove a specific index without leaving holes in the array
const target = this.props.data.indexOf(oldData);
const newData = this.props.data.filter((_, index) => index !== target);
2) Remove a specific value from the array (all its occurrences) without leaving holes in the array
const newData = this.props.data.filter((data) => data !== oldData);
Those two are slightly different as the first one will only remove the first occurrence of oldData and the second all
occurrences.

Assigning values whilst creating a 2D array

Here is some Javascript code that creates a 2-dimension array and fills each cell with a random number.
// populate 2D array with integers
const row = 5, col = 7
let pic = new Array(row).fill(0).map(item => (new Array(col).fill(0)))
for (let x = 0; x < row; x++) {
for (let y = 0; y < col; y++) {
pic[x][y] = Math.floor((Math.random() * 90) + 10)
}
}
console.log(JSON.stringify(pic))
I'm looking for a more 'elegant' solution. My questions:
Is there a way to use the fill so that I can put in my target values? Then I can be finished with creating the array in one line.
How do I use a double .map to populate the 2D array, instead of a double for loop?
Is there a way to assign the output from the map / for loops directly into a variable? Then I don't need a separate create statement.
What is the best way to reshape an array? For example, changing a 1-by-10 array into a 5-by-2 array.
Is there a way to enforce a type? For instance the first dimension is a string, 2nd is an integer, etc.
Feel free to add your own definition of elegance. One of the things I'm looking for is a flexible approach that can also work with 3D arrays.
You could take a nested Array.from with a length and a mapping function.
const
fn = () => Math.floor(Math.random() * 5),
row = 5,
col = 7,
array = Array.from({ length: row }, () => Array.from({ length: col }, fn));
array.forEach(a => console.log(...a));
Is there a way to use the fill so that I can put in my target values? Then I can be finished with creating the array in one line.
No, fill is not that flexible. There is Array.from(iterable, callback) but I find it cumbersome and it is slow. I'd rather write that utility quickly
function array(n, callback){
const output = Array(n);
for(let i=0; i<n; ++i) output[i] = callback(i);
return output;
}
How do I use a double .map to populate the 2D array, instead of a double for loop?
map creates a new Array, by calling the callback function for each item on the current Array. You can abuse it to mutate the Array that is iterating. You can ignore the returnes Array and abuse it as forEach; but then map simply is the wrong tool.
var newMatrix = Array(5).fill().map(() => Array(7).fill().map(() => Math.random()));
the fill part is necessary, because Array(length) creates a sparse Array of that length and map only iterated defined indices (even if they contain undefined)
Is there a way to assign the output from the map / for loops directly into a variable? Then I don't need a separate create statement.
I'm not sure what you mean, because you already do that here let pic = new Array(row).fill(0).map(...)
What is the best way to reshape an array? For example, changing a 1-by-10 array into a 5-by-2 array.
function array(n, callback) {
const output = Array(n);
for (let i = 0; i < n; ++i) output[i] = callback(i);
return output;
}
function toGroupsOf(n, data) {
return array(Math.ceil(data.length / n), i => data.slice(n * i, n * (i + 1)));
}
const oneByTen = [array(10, v => v)];
console.log(oneByTen);
const twoByFive = toGroupsOf(5, oneByTen.slice().flat());
console.log(twoByFive);
Is there a way to enforce a type? For instance the first dimension is a string, 2nd is an integer, etc.
No, not in JS. btw. everything but the last dimension will be Arrays, not String.
But check out Typescript.
Feel free to add your own definition of elegance. One of the things I'm looking for is a flexible approach that can also work with 3D arrays.
// a general purpose function to create n-dimensional arrays.
// m(...dimensions, (...indices) => value)
function m(...args) {
return args.reduceRight((cb, length) => (...indices) => {
const output = Array(length);
for (let i = 0; i < length; ++i)
output[i] = cb(...indices, i);
return output;
})();
}
let data = m(5,7, () => Math.floor(Math.random() * 90 + 10));
console.log(data);
// 4-dimensions
console.log(m(2,3,4,5, Math.random));
// a 5x5 identity-matrix
console.log(m(5,5, (i,j) => i === j? 1: 0).join("\n"));
I'm a user of strongly typed languages like Scala, where for instance, you could never store a string in an integer variable. I find the laissez faire of Javascript difficult.
I have mixed opinions on that. I loved the way that static types and compile-time errors found little mistakes/oversights back when I learned (in AS3). Nowadays and with Typescript I often find Typescript to be too opinionated and find myself thinking f off compiler, I know/mean what I'm writing here and prefer the flexibility of JS. On the other hand, I still enjoy the assistance that comes from the IDE knowing what Objects I'm currently dealing with and what properties/methods they provide.
Heavily inspired by: https://stackoverflow.com/a/53859978/9758920
const row = 5, col = 7;
let pic = [...Array(row)].map(r => [...Array(col)].map(c => ~~(Math.random()*90)+10));
console.log(pic)
This should work.
const randomNumber = Math.floor((Math.random()*90)+10);
const randomMatrix = (row, col) => {
return new Array(row).fill(randomNumber).map(item => (new Array(col).fill(randomNumber)))
}
console.log(randomMatrix(5, 7))
Try the snippet below. initializeArray accepts parameters for width, height and a value for each cell.
const initialize2DArray = (w, h, val = null) =>
Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
console.log(initialize2DArray(3, 3, 0)) // 3x3 matrix filled with zeros
If you prefer a N-dimension array, try the snippet below:
const initializeNDArray = (val, ...args) =>
args.length === 0
? val
: Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1)));
console.log(initializeNDArray(-1, 3, 3, 3))

How to group multiple sets of duplicate integers in an array into their own array of arrays?

I am trying to split an array of integers into an array of arrays by duplicate values. The original array is composed of a list of 6 digit integers, some of these integers come in pairs, others come in groups of 3 or 4s. I'd like to get these duplicates pushed to their own arrays and have all of these arrays of duplicates composed into an array of arrays that I can later loop through.
I've looked on in the lodash library for some method or combination of but can't quite find anything that seems to work. I've also tried a few different configurations with nested for loops but also am struggling with that.
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = [ ];
for(i=0; i< files.length; i++){
let first6 = files[i].substring(0, 6);
first6Array.push(first6);
};
console.log(first6Array);
example output of first6Array:
[ '141848',
'141848',
'141848',
'142851',
'142851',
'143275',
'143275']
I'd like to end up with something like
let MasterArray = [[141848,141848,141848],[142851,142851],[143275,143275]];
You can use new Set() to filter out the duplicates.
Then you use the unique Array and filter for every value.
const firstArray = [ '141848', '141848', '141848', '142851', '142851', '143275', '143275'];
const numberArray = firstArray.map(Number);
const masterArray = [];
const unique = new Set (numberArray); // Set {141848, 142851, 143275}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Using lodash, you can create a function with flow:
map the items by truncating them and converting to numbers.
groupBy the value (the default).
convert to an array of arrays using values.
const { flow, partialRight: pr, map, truncate, groupBy, values } = _;
const truncate6 = s => truncate(s, { length: 6, omission: '' });
const fn = flow(
pr(map, flow(truncate6, Number)),
groupBy,
values,
);
const firstArray = [ '141848abc', '141848efg', '141848hij', '142851klm', '142851opq', '143275rst', '143275uvw'];
const result = fn(firstArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Use reduce to create an object of arrays, indexed by number, and push to the associated array on each iteration (creating the array at the key first if needed), then get the values of the object:
const directory = "X/";
const files = fs.readdirSync(directory);
const output = Object.values(
files.reduce((a, file) => {
const num = Number(file.slice(0, 6));
if (!a[num]) a[num] = [];
a[num].push(num);
return a;
}, {})
);
It's pretty weird to have an array of identical values, though - you might consider a different data structure like
{
'141848': 3,
'142851': 2
}
to keep track of the number of occurrences of each number:
const output = files.reduce((a, file) => {
const num = file.slice(0, 6);
a[num] = (a[num] || 0) + 1;
return a;
}, {})
To obtain exactly the result you desire, you need a nested find, something like this should works:
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = files.reduce((acc, value)=> {
let n = +value.substr(0, 6); // assumes it can't be NaN
let arr = acc.find(nested => nested.find(item => item === n));
if (arr) {
arr.push(n);
} else {
acc.push([n]);
}
return acc;
}, []);
console.log(first6Array);
Notice that an hashmap instead, with the value and the number of occurrence, would be better, also in term of performance, but I don't think it mind since you have really few elements.
Also, it assumes the first six characters are actually numbers, otherwise the conversion would fail and you'll get NaN.
It would be safer adding a check to skip this scenario:
let n = +value.substr(0, 6);
if (isNaN(n)) {
return acc;
}
// etc

Insert a string. Create new string which is the mirror reverse of the inserted one around its center

The formula must work in these words.
Input Output
“stranger” “ngerstra”
“rotator” “torarot”
The Basic Idea
So with this solution, it's quite simple, you have your dictionary, then you make a slight change to each object within the dictionary, you basically add some string to it so that if a word is 'card', it'll swap the order of the letters in such a way that when you search for 'drac', card will appear to be a similar word.
A bit more detail
When you search for words similar to what you've got in your dictionary, to prevent outputting exactly what you've already searched, when constructing a new array, it'll push null onto the array if index x is the same word as the word searched.
Then, once the array has been constructed, it filters out all values set to null.
// Simple string sort function.
const sort = s => [...s].filter(x => x != null).sort().join('');
// Helper function which will state whether or not to push
// a string onto the array or push null onto the array.
const push = o => w => sort(o) === sort(w) && o != w ? o : null;
// A function that takes an array of words and a word
// via the use of currying, then magic happens!
const anagrams = d => w => d.map(o => push(o)(w)).filter(s => s != null);
// The proof is in the pudding...
const dictionary = ['stranger','ngerstra','torarot','rotator'];
const word = 'ngerstra';
const otherWord = 'rotator';
const results = anagrams(dictionary)(word);
const otherResults = anagrams(dictionary)(otherWord);
console.log(results);
console.log(otherResults);

Categories