Related
I have an object like this:
const arrays = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
As you can see each first number is equal:
1 for each first inner array
2 for each second inner array
3 for each third inner array
etc...
I want to filter based on the first number of each array
and some comparator number e.g. [3]
If we have a filter number [3] (smaller or equal to 3),
the wanted result would be:
const arrays = {
one: [[1,33,41], [2,0,27], [3,7,9]],
two: [[1,77,2], [2,6,3], [3,0,0]],
three: [[1,4,6], [2,0,0], [3,5,6]],
};
Since all first numbers of inner arrays are smaller than or equal to 3.
The arrays starting with 4,5... are ignored.
What would be the ramda's way to have this functionality?
I like Ramda's map function because it can iterate over the properties of an object (and so avoid Object.fromEntries & Object.entries) and apply a function to each one of them. That function is filter which will take as argument the inner arrays. The function given to filter is itself a composition of gte and head; takes the first element of an array and compare it with 3:
const arrays =
{ one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]]
, two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]]
, three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]] };
map(filter(compose(gte(3), head)), arrays);
// ^ ^ ^ ^ ^
// A B C D E
//=> { one: [[ 1, 33, 41], [2, 0, 27], [3, 7, 9]]
//=> , two: [[ 1, 77, 2], [2, 6, 3], [3, 0, 0]]
//=> , three: [[ 1, 4, 6], [2, 0, 0], [3, 5, 6]] }
map over each property (A); each array is passed to filter (B)
Each inner array is passed to compose (C)
Take the head of each inner array (E) and compare with 3 (D)
Scott Christopher rightly pointed out in the comments that gte can be confusing when partially applied. In fact the whole composition can be replaced with this simple lambda: ([x]) => x <= 3.
Alternative solution which I like too:
map(filter(([x]) => x <= 3), arrays);
I'd totally subscribe for #customcommander's approach,
just wanted to add that you can also pass numerical indexes to R.propSatisfies.
const headIs3OrBelow = R.propSatisfies(R.gte(3), 0);
const fn = R.map(R.filter(headIs3OrBelow));
// ===
const data = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Also agree that gte and other similar methods are very difficult to read, because they kind of read backwards as is 3 gte than x... in Haskell you could do something like:
3 `gte` x
Vanilla approach:
const headIs3OrBelow = ([head]) => head <= 3;
const fn = (data) => Object.entries(data).reduce(
(res, [k, lists]) => ({ ...res, [k]: lists.filter(headIs3OrBelow) }),
{},
);
// ===
const data = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
console.log(
fn(data),
);
I understand you would like to use Ramda, this is not a solution using the library but accomplishes the same. You could create an object from entries that are filtered by comparing the first array value a[0] to the maxVal passed to the function.
const arrays = {
one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
};
const filterArrays = (arsObj, maxVal) => {
return Object.fromEntries(Object.entries(arsObj).map(([k, v]) => {
return [k, v.filter((a) => a[0] <= maxVal)];
}));
}
const result = filterArrays(arrays, 3);
console.log(result);
in my project I have to turn a bunch of coordinates to some meaningful two-dimensional array but I really don't know how to do it. Can somebody help?
To explain what I exactly want, let me give an example:
Let's suppose that I have these 2 arrays(the reason that I started from one is because 0 and the last element of my rows are borders):
[[1, 1], [1, 2], [1, 4], [1, 5], [1, 8], [1, 9], [1, 10],[2, 1], [2, 2], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 10]]
Let the value inside these coordinates be like [row,col]. And let's say I wan't to match them to generate some sort of two-dimensional array and each of the elements should contain the value '#'. However, for example;
[1, 2], [1, 4]
[2, 2], [2, 4]
If there's a coordinate missing between two of these elements, they should be separated, meaning that there should be two different two-dimensional arrays, being split from that coordinate. In this case, the result should be;
// First two-dimensional array
const firstArray = [
['#','#'],
['#','#']
]
const secondArray = [
['#','#','','','#','#','#'],
['#','#','#','#','#','','#'],
]
In the second array, there are some '' values, but that is because the there are some coordinates missing(for [1, 5] and [1, 8], [1,6] and [1,7] are missing). So that should be considered too.
If you didn't understand please comment under the question me so that I can explain it to you.
How can I come up with the functionality that I'm looking for?
You can accomplish both steps with a single Array#reduce() call by using the coordinates themselves to place each [row, col] in its relevant place in the matrix.
Here using an OR short circuit for assigning new sub-arrays, with a commented out replacement using the logical nullish assignment operator (??=), and the comma operator for shorthand return in the arrow function.
const coords = [[1, 1], [1, 2], [1, 4], [1, 5], [1, 8], [1, 9], [1, 10], [2, 1], [2, 2], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 10]];
const matrix = coords.reduce((acc, [row, col]) => (
// using OR short circuit for compatibility
(acc[row - 1] || (acc[row - 1] = []))[col - 1] = [row, col], acc
// using logical nullish assignment operator (??=)
//(acc[row - 1] ??= [])[col - 1] = [row, col], _matrix
), [])
// logging
for (const row of matrix) {
console.log(`[[${row.join('], [')}]]`)
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
const input = [[1, 1], [1, 2], [1, 4], [1, 5], [1, 8], [1, 9], [1, 10],[2, 1], [2, 2], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 10]]
const result = input.reduce((acc, [x, y]) => {
acc[x - 1] ??= []
const previousY = acc[x - 1][acc[x-1].length - 1];
if (previousY) {
const delta = y - previousY;
if (delta > 1) acc[x-1].push(...Array.from({length: delta - 1}));
}
acc[x-1].push(y);
return acc
}, [])
console.log('1234567890')
console.log(
result.map(row =>
row.map(coor => coor ? '#' : ' ').join('')
).join('\n'))
I am trying to replicate an array of arrays and then modify the same element of each sub-array.
The following code is used to replicate the initial array of arrays:
const array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const n = 2; // replicate twice
let replicated_arrays = [];
for (let i = 0; i < n; i++) {
replicated_arrays.push(array);
}
replicated_arrays = [].concat.apply([], replicated_arrays); // flatten to make one array of arrays
The following code is then used to modify the second element of each array:
const init = 10;
replicated_arrays.forEach(function(element, index, entireArray) {
entireArray[index][1] = init + index;
});
The desired output is:
[[1, 10, 3], [4, 11, 6], [7, 12, 9], [1, 13, 3], [4, 14, 6], [7, 15, 9]]
However, the above code produces the following:
[[1, 13, 3], [4, 14, 6], [7, 15, 9], [1, 13, 3], [4, 14, 6], [7, 15, 9]]
The forEach updates properly if the replicated array is created manually:
let replicated_arrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 8, 9]];
I therefore suspect it has something to do with the push method creating a reference to both instances of the initial array such that the final set of values (13, 14, and 15) are applied to both instances.
As an alternative to the push method, I tried the map method (e.g., in accordance with Duplicate an array an arbitrary number of times (javascript)), but it produced the same result.
Any insight or suggestions as to what is going on or how to make it work properly would be appreciated.
You need to take copies of the inner arrays, because you need to lose the same object reference.
For pushing, you could spread the array and omit flattening later.
const array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const n = 2; // replicate twice
let replicated_arrays = [];
for (let i = 0; i < n; i++) {
replicated_arrays.push(...array.map(a => a.slice())); // spread array
}
// no need for this! replicated_arrays = [].concat.apply([], replicated_arrays);
const init = 10;
replicated_arrays.forEach(function(element, index) {
element[1] = init + index; // access element directly without taking the outer array
});
console.log(replicated_arrays);
Instead of concat use reduce method will keep same reference.
const array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const n = 2; // replicate twice
let replicated_arrays = [];
for (let i = 0; i < n; i++) {
replicated_arrays.push(array);
}
replicated_arrays = replicated_arrays.reduce(function(a, b){
return a.concat(b);
}, []);
replicated_arrays.forEach((_ae,i) => {
_ae[1] = 10 + i;
})
console.log(replicated_arrays);
output: [[1, 10, 3], [4, 11, 6], [7, 12, 9], [1, 13, 3], [4, 14, 6], [7, 15, 9]]
Now I am working on a exercise in freecodecamp. Currently I got an logical error but do not why the failure happens.
In the code,I have to build in a function, which chop the input array based on the parameter. The testing result should be as follows:
chunkArrayInGroups(["a", "b", "c", "d"], 2) should return [["a", "b"], ["c", "d"]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3) should return [[0, 1, 2], [3, 4, 5]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4) should return [[0, 1, 2, 3], [4, 5, 6, 7], [8]].
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]].
And my code are as follows:
function chunkArrayInGroups(arr, size) {
var array = [];
for (var x = 0; x < arr.length ; x+=size){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
array.push(arr);
return array;
}
chunkArrayInGroups(["a", "b", "c", "d","e"], 2);
For most of the conditions, the code works. But for the last condition i.e
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2) should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]].
in this case I cannot get the correct answer. I tested in console log, and turn out the output is like
[[0, 1], [2, 3], [4, 5], [6, 7, 8]].
I know that it is not a difficult question and there are lots of better way to approach it, but can I know what is the logic fallancy in this code?
Many thanks!
Instead of splice use slice. This will also guarantees that the original array is not modified.
Like this (working demo):
function chunkArrayInGroups(arr, size) {
var array = [];
for (var x = 0; x < arr.length; x += size) {
// take elements from current index (`x`) to `x` + `size`
// (do not remove them from the original array, so the original size is not modified either)
var sliceArr = arr.slice(x, x + size);
array.push(sliceArr);
}
return array;
}
console.log(chunkArrayInGroups(["a", "b", "c", "d"], 2)); //should return [["a", "b"], ["c", "d"]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3)); // should return [[0, 1, 2], [3, 4, 5]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4)); // should return [[0, 1, 2, 3], [4, 5, 6, 7], [8]].
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2)); // should return [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
It might help to add a console.log(arr) to your loop to see how the array changes over time.
You would see that it looks like this:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[2, 3, 4, 5, 6, 7, 8]
[4, 5, 6, 7, 8]
Then, take into account your final splice and add which occurs outside of the loop:
[6, 7, 8]
Since your loop increments by size, it will exit once it has gathered all subarrays of exactly size.
Instead, I would recommend continuing until your input is empty:
function chunkArrayInGroups(arr, size) {
var array = [];
while(arr.length > 0){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
return array;
}
You will want to step using the size to save on the number of loops through the array. We are also saving the length so it's not fetched each time as it saves operations. Also you will notice that I'm not using var as you shouldn't be using it. Please use let for normal variables and const for variables you are not going to reassign.
function chunkArrayInGroups(arr, size) {
let array = [];
let arrayLength = arr.length;
for (let i = 0; i < arrayLength; i+=size) {
array.push(arr.slice(i, i+size));
}
return array
}
console.log(chunkArrayInGroups(["a", "b", "c", "d"], 2), [["a", "b"], ["c", "d"]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5], 3), [[0, 1, 2], [3, 4, 5]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4), [[0, 1, 2, 3], [4, 5, 6, 7], [8]])
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2), [[0, 1], [2, 3], [4, 5], [6, 7], [8]])
The issue here is that you are reducing the array length throughout your iteration. I.e. your array gets smaller within each iteration while your x continously increases. That means that before your last iteration your x will be at 6 and the array length will be 3, hence x < arr.length evaluates to false and your last iteration does not happen. The most simplistic solution that I can think of is to store the original array length into a variable I named stop and remove the unneccessary final array push outside the loop.
function chunkArrayInGroups(arr, size) {
var array = [];
var stop = arr.length;
for (var x = 0; x < stop; x+=size){
var spliceArr = arr.splice(0,size);
array.push(spliceArr);
}
return array;
}
console.log(chunkArrayInGroups([1,2,3,4,5,6,7], 2))
splice method changes the length of array on every iteration. That's why your loop exits before you expect. You can read more about splice here.
Unlike splice, slice will not remove items from the array that's why lealceldeiro answer will work as expected.
Kevin Bruccoleri answer looks cleaner and shorter but if you have an app where you store an array in to a variable and then pass it to the function, that variable will be empty after the execution of the function, which can lead to bugs in your app. That's why arrays are basically object, but that's science fiction of javascript.
function chunkArrayInGroups(arr, size) {
var array = [];
while (arr.length) {
array.push(arr.splice(0, size))
}
return array
}
var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8]
console.log('now it full', nums);
console.log(chunkArrayInGroups(nums, 2));
console.log('now it empty', nums);
Used slice to copy original array two
map() and splice() to insert array from n index
const frankenSplice = (arr1, arr2, n) => {
let arr = arr2.slice();
arr1.map(e => {
arr.splice(n, 0, e);
n++;
})
return arr;
}
How to simply flatten array in jQuery? I have:
[1, 2, [3, 4], [5, 6], 7]
And I want:
[1, 2, 3, 4, 5, 6, 7]
You can use jQuery.map, which is the way to go if you have the jQuery Library already loaded.
$.map( [1, 2, [3, 4], [5, 6], 7], function(n){
return n;
});
Returns
[1, 2, 3, 4, 5, 6, 7]
Use the power of JavaScript:
var a = [[1, 2], 3, [4, 5]];
console.log( Array.prototype.concat.apply([], a) );
//will output [1, 2, 3, 4, 5]
Here's how you could use jquery to flatten deeply nested arrays:
$.map([1, 2, [3, 4], [5, [6, [7, 8]]]], function recurs(n) {
return ($.isArray(n) ? $.map(n, recurs): n);
});
Returns:
[1, 2, 3, 4, 5, 6, 7, 8]
Takes advantage of jQuery.map as well as jQuery.isArray.
var a = [1, 2, [3, 4], [5, [6, [7, 8]]]];
var b = [];
function flatten(e,b){
if(typeof e.length != "undefined")
{
for (var i=0;i<e.length;i++)
{
flatten(e[i],b);
}
}
else
{
b.push(e);
}
}
flatten(a,b);
console.log(b);
The flatten function should do it, and this doesn't require jQuery. Just copy all of this into Firebug and run it.
To recursively flatten an array you can use the native Array.reduce function. The is no need to use jQuery for that.
function flatten(arr) {
return arr.reduce(function flatten(res, a) {
Array.isArray(a) ? a.reduce(flatten, res) : res.push(a);
return res;
}, []);
}
Executing
flatten([1, 2, [3, 4, [5, 6]]])
returns
[ 1, 2, 3, 4, 5, 6 ]
You can use jQuery.map():
callback( value, indexOrKey )The function to process each item
against. The first argument to the function is the value; the second
argument is the index or key of the array or object property. The
function can return any value to add to the array. A returned array
will be flattened into the resulting array. Within the function, this
refers to the global (window) object.
Use recursion if you have multiple levels:
flaten = function(flatened, arr) {
for(var i=0;i<arr.length;i++) {
if (typeof arr[i]!="object") {
flatened.push(arr[i]);
}
else {
flaten(flatened,arr[i]);
}
}
return;
}
a=[1,[4,2],[2,7,[6,4]],3];
b=[];
flaten(b,a);
console.log(b);
You can use Array.prototype.reduce which is technically not jQuery, but valid ES5:
var multidimensionArray = [1, 2, [3, 4], [5, 6], 7];
var initialValue = [];
var flattened = multidimensionArray.reduce(function(accumulator, current) {
return accumulator.concat(current);
}, initialValue);
console.log(flattened);
Old question, I know, but...
I found this works, and is fast:
function flatten (arr) {
b = Array.prototype.concat.apply([], arr);
if (b.length != arr.length) {
b = flatten(b);
};
return b;
}
You need arr.flat([depth])
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]