Creating a 2d array that consisted of another 2d array's acumulation - javascript

I have an array which is like that: [[0, 50], [1, 40], [2, 30], [3, 20], [5, 10]]
And I want to accumulative the second values: [[0, 50], [1, 90], [2, 120], [3, 140], [5, 150]]
I tried the code part below which works for one dimensional arrays, but it doesn't work for 2d arrays. Is it possible to accumulate it by using reduce function? Or is there different way to do it?
var array1 = [[0, 50], [1, 40], [2, 30], [3, 20], [5, 10]];
var newArray1 = [];
array1.reduce(
function (previousValue, currentValue, currentIndex) {
return newArray1[currentIndex] = [currentIndex, (previousValue[1] + currentValue[1])];
}, 0
);

You can use map() with optional thisArg parameter
var array1 = [[0, 50], [1, 40], [2, 30], [3, 20], [5, 10]];
var result = array1.map(function(e) {
this.num = (this.num || 0) + e[1];
return [e[0], this.num];
}, {});
console.log(result);

Use Array#reduce method
var array1 = [
[0, 50],
[1, 40],
[2, 30],
[3, 20],
[5, 10]
];
// initialize as the array of first element in original array
var newArray1 = [array1[0].slice()];
array1
// get remaining array element except first
.slice(1)
// iterate over the array value to generate result array
.reduce(function(arr, v, i) {
// copy the array element if you don't want to refer the old
v = v.slice();
// add previous array value
v[1] += arr[i][1];
// push updated array to result array
arr.push(v);
// retur the updated array
return arr;
// set initial value as array which contains first element(array) copy
},newArray1);
console.log(newArray1)
UPDATE 1: Another method with less code
var array1 = [
[0, 50],
[1, 40],
[2, 30],
[3, 20],
[5, 10]
];
var newArray1 = [array1[0].slice()];
array1.slice(1).reduce(function(arr, v, i) {
arr.push([v[0], v[1] + arr[i][1]]);
return arr;
}, newArray1);
console.log(newArray1)
UPDATE 2 : Much more reduced version without using Array#slice method.
var array1 = [
[0, 50],
[1, 40],
[2, 30],
[3, 20],
[5, 10]
];
var newArray1 = array1.reduce(function(arr, v, i) {
// push value to array add value only if `arr` contains any element
arr.push([v[0], v[1] + (arr.length && arr[i - 1][1])]);
return arr;
// set initial value as an empty array
}, []);
console.log(newArray1)

Just for fun lets invent a new array functor, Array.prototype.extend() This works like opposite to the reduce. It takes an array and extends it starting from the last item by utilizing a provided callback. When the callback returns undefined it sops extending. Let see how we can have fun with it in this particular case;
Array.prototype.extend = function(cb){
var len = this.length + 1,
res = cb(this[len-1], len-1, this);
return res ? this.extend(cb) : this;
};
var arr = [[0, 50], [1, 40], [2, 30], [3, 20], [5, 10]],
cb = function(e,i,a){
return i === 0 ? a.push(arr[i])
: i < arr.length ? a.push([arr[i][0], arr[i][1] + a[i-1][1]])
: void 0;
};
result = [].extend(cb);
console.log(result);

Related

Line combination algorithm

array = [[1, 2], [13, 14], [4, 5], [80, 30], [12, 14], [10, 90], [3, 2], [6, 9], [1, 5], [4, 5], [5, 9], [4, 3], [13, 12]]
//expected
output = [[1, 2], [13, 14], [4, 5], [80, 30], [10, 90], [3, 2], [6, 9], [4, 5], [5, 9], [4, 3], [13, 12]]
You can consider the subarrays as lines, for example, [1,2] would be a line connected from point 1 to point 2. Therefore, [1,2],[3,2],[4,3],[4,5],[4,3] would correlate with several short lines that connect point 1 to point 5, because there is a line connected from point 1 to 2, 2 to 3, 3 to 4, and 4 to 5.
If the array contains a larger single line that is point 1 to point 5, it should be filtered out. This is to remove all longer lines that already have their points defined in more shorter lines. What algorithm could be used to solve this?
I have tried the code below at https://codepen.io/Maximusssssu/pen/jOYXrNd?editors=0012
The first part outputs all subarrays in ascending order for readability, whereas for the second part, I have tried the include() method to check whether a node is present in the subarray, and get its position.
array = [[1, 2], [13, 14], [4, 5], [80, 30], [12, 14], [10, 90], [3, 2], [6, 9], [1, 5], [4, 5], [5, 9], [4, 3], [13, 12]]
array_ascending_arr = [];
for (let i = 0; i < array.length; i++) {
let subarrayy = array[i].sort(function(a, b) {
return a - b
});
array_ascending_arr.push(subarrayy);
}
console.log(array_ascending_arr) // output all subarrays in ascending order back into array
for (let k = 1; k < 5; k++) {
for (let m = 0; m < array.length; m++) {
for (let i = 1; i < 2; i++) {
if (array_ascending_arr[m].includes(k) == true) {
console.log(m)
}
}
}
console.log(".......")
}
the main idea is to calculate the difference in number of intermediate elements and not in value
if we have [[1,2],[14,10],[4,6],[9,2] that makes a sequence = [1,2,4,6,9,10,14] (sorted)
return delta values:
[1,2] -> d:1
[9,2] -> d:3 // the 9 is three positions away from the 2
the principle is therefore to process starting from the least distant values towards those most distant
(the other sorting criteria are secondary and are mainly useful for debugging)
note: duplicate pairs are also eliminated
const
test1 = [[1,2],[13,14],[4,5],[80,30],[12,14],[10,90],[3,2],[6,9],[1,5],[4,5],[5,9],[4,3],[13,12]]
, test2 = [[1,2],[4,5],[30,80],[12,18],[10,90],[2,3],[6,9],[1,5],[4,6],[5,9],[3,4],[12,13],[12,14],[14,15],[15,18]]
;
console.log('test1:\n', JSON.stringify( combination( test1 )))
console.log('test2:\n', JSON.stringify( combination( test2 )))
function combination(arr)
{
let
nodes = arr.flat().sort((a,b)=>a-b).filter((c,i,{[i-1]:p})=>(c!==p))
, sets = nodes.map(g=>[g])
, bads = []
;
arr // i:index, s:start(min), e:end(max), d: delta
.map(([v0,v1],i) => ({i,s:Math.min(v0,v1),e:Math.max(v0,v1), d:Math.abs(nodes.indexOf(v0) - nodes.indexOf(v1))}))
.sort((a,b) => (a.d-b.d) || (a.s-b.s) || (a.e-b.e) || (a.i-b.i) )
.forEach(({i,s,e,d}) =>
{
let
gS = sets.find(n=>n.includes(s))
, gE = sets.find(n=>n.includes(e))
;
if (gS === gE) { bads.push(i) }
else { gS.push(...gE); gE.length=0 }
})
//console.log( sets.filter(a=>a.length).map(a=>JSON.stringify(a)).join(' - ') )
return arr.filter((x,i)=>!bads.includes(i))
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row::after { display: none !important; }
You could try for an adjacency list. My understanding of the algorithm is if two pairs share an edge they will combine to form a new edge until all sets are unique.
So from what I understand, you're trying to remove any set of numbers that encompasses a range greater than any that are smaller. The following code should do this:
array.filter(pair => {
// Get the smaller and larger numbers from the pair
const [small, big] = pair.sort((a,b) => a-b)
// Check if this pair is larger than any others
return !array.some(test => {
const [testSmall, testBig] = test.sort((a,b) => a-b)
return big > testBig && small < testSmall
})
})
Note that this won't remove duplicates.
If you don't mind your subarrays being reordered in the final array, you can simplify it a bit by sorting them all at the beginning:
array
.map(pair => pair.sort((a,b) => a-b))
.filter(([s, b], _, arr) => !arr.some(([ts, tb]) => b>tb && s<ts))

How to modify value of n dimensional array element where indices are specified by an array in Javascript

I have an n-dimensional array and I want to access/modify an element in it using another array to specify the indices.
I figured out how to access a value, however I do not know how to modify the original value.
// Arbitrary values and shape
arr = [[[8, 5, 8],
[9, 9, 9],
[0, 0, 1]],
[[7, 8, 2],
[9, 8, 3],
[9, 5, 6]]];
// Arbitrary values and length
index = [1, 2, 0];
// The following finds the value of arr[1][2][0]
// Where [1][2][0] is specified by the array "index"
tmp=arr.concat();
for(i = 0; i < index.length - 1; i++){
tmp = tmp[index[i]];
}
// The correct result of 9 is returned
result = tmp[index[index.length - 1]];
How can I modify a value in the array?
Is there a better/more efficient way to access a value?
This is a classic recursive algorithm, as each step includes the same algorithm:
Pop the first index from indices.
Keep going with the array that the newly-popped index points to.
Until you get to the last element in indices - then replace the relevant element in the lowest-level array.
function getUpdatedArray(inputArray, indices, valueToReplace) {
const ans = [...inputArray];
const nextIndices = [...indices];
const currIndex = nextIndices.shift();
let newValue = valueToReplace;
if (nextIndices.length > 0) {
newValue = getUpdatedArray(
inputArray[currIndex],
nextIndices,
valueToReplace,
);
} else if (Array.isArray(inputArray[currIndex])) {
throw new Error('Indices array points an array');
}
ans.splice(currIndex, 1, newValue);
return ans;
}
const arr = [
[
[8, 5, 8],
[9, 9, 9],
[0, 0, 1]
],
[
[7, 8, 2],
[9, 8, 3],
[9, 5, 6]
]
];
const indices = [1, 2, 0];
const newArr = getUpdatedArray(arr, indices, 100)
console.log(newArr);
You can change the values in array like this,
arr[x][y][z] = value;
Does this help?
I think what you're looking for is this:
arr[index[0]][index[1]][index[2]] = value;
I'm having trouble understanding what you're attempting to do in the second part of your example.

How to get the last array that includes a certain element?

I have an array of arrays and I want to check if there is a tie between the second elements and then return the first element of the last array that makes a tie.
for example this should return 4. (the first element in the last array that has a second element that makes a tie)
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
It is quite simple, you need to iterate over your source array, check if the given item matches the criteria, and save it to result if it does. Now if any other item does match the criteria, result's value will be overwritten with the new matching item.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
optionsArray.forEach(function(item) {
if(item[1] == 10) {
result = item;
}
});
console.log(result);
You can create a simple find function that iterates the array backwards, and returns as soon as a condition callback returns true.
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
function find10(s) {
return s[1] === 10;
}
function findFromTheEnd(arr, cb) {
var l = arr.length;
while(l--) { // iterate backwards
if(cb(arr[l])){ // if the callback returns true
return arr[l]; // return the item
}
}
return null; // return null if none found
}
var result = findFromTheEnd(optionsArray, find10);
console.log(result);
You can use reduceRight() and return array.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result = arr.reduceRight(function(r, e) {
if(e[1] == 10 && !r) r = e;
return r;
}, 0)
console.log(result)
You can also use for loop that starts from end and break on first match.
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var result;
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i][1] == 10) {
result = arr[i]
break;
}
}
console.log(result)
A classic for in the reserve order with a break seems enough :
var optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
var elementFound;
for (var i = optionsArray.length-1; i >=0; i--) {
if(optionsArray[i].item[1] == 10) {
elementFound = optionsArray[i].item[1];
break;
}
}
If elementFound is not undefined, it refers to the found array.
Rather than considering this as a multidimensional array problem, think of it as an array includes problem nested in an array search problem;
const aarr = [1, 2, 3, 4];
aarr.includes(3); // true
aarr.includes(10); // false
// and
const barr = ['hello', 'world'];
barr.find(item => item[0] === 'h'); // "hello"
barr.find(item => item[3] === 'l'); // "hello"
barr.find(item => item[1] === 'z'); // undefined
So to nest these,
const carr = [[1, 2, 3, 4], [4, 5, 6, 7]];
carr.find(arr => arr.includes(4)); // [1, 2, 3, 4]
carr.find(arr => arr.includes(6)); // [4, 5, 6, 7]
Next, we've reduced the whole problem down to "how to do this in reverse?"
You've a few options depending on how you want to implement it, but a simple way to do it is a shallow clone arr.slice() followed by a reverse arr.reverse() (we use the clone so there are no side-effects of reverse on the original array)
carr.slice().reverse().find(arr => arr.includes(4)); // [4, 5, 6, 7]
If you're working with an index, remember that you'll need to transform those too; -1 is fixed, otherwise transformed_index = arr.length - original_index - 1
Here is how you might implement the reverse of some of the Array methods
const optionsArray = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]];
// index 0 1 2 3 4
function arrLast(arr, comparator, method = 'find', transform = x => x) {
return transform(arr.slice().reverse()[method](comparator), arr);
}
const findLast = (arr, comparator) => arrLast(arr, comparator);
const findLastIndex = (arr, comparator) => arrLast(arr, comparator, 'findIndex', (i, arr) => i === -1 ? -1 : arr.length - i - 1);
arrLast(optionsArray, arr => arr.includes(10)); // [4, 10]
findLastIndex(optionsArray, arr => arr.includes(10)); // 3
If you have to make comparisons among array items and you need to cut short once you are satisfied a while loop is ideal. Accordingly you may do as follows;
var arr = [[1, 10], [2, 10], [3, 10], [4, 10], [6, 14]],
i = 0,
result;
while (arr[i][1] === arr[++i][1]);
result = arr[i-1][0]
console.log(result);

JavaScript: Map issue

I have an issue with my code.
function openOrSenior(data) {
"use strict";
let myMap = new Map(data);
let test = [];
let old = [];
let val = [];
for(let key of myMap.keys()){
old.push(key);
}
for(let value of myMap.values()){
val.push(value);
}
for(let i = 0; i < data.length; i++){
if(old[i] >= 55 && val[i] > 7){
test.push("Senior");
} else { test.push("Open");}
}
return test;
}
If I have an input like this and debug my code:
openOrSenior([[21, 21], [0, 0], [90, 8], [1, 1], [60, 12], [90, 7], [75, 11], [55, 10], [90, 9], [54, 9]]);
My map have this key&value pairs :myMap
It's not in the right sequence and does not contain all key-->values pairs, but if I take a subset of my input like this:
openOrSenior([[21, 21], [0, 0], [90, 8]]);
The Code does what I want:Here is the picture
What am I doing wrong?
Best Regards
As I commented above, a Map has unique keys. You can however extend the Map class and create a Multimap. A very (very very) simple implemantation is found below. Below will add all the values to an array found at key, pushing to the existing value if the key is already in our Multimap, you can add elements by using set()
class Multimap extends Map {
constructor(arrOfArr = [[]]) {
super(arrOfArr);
}
set(key, value) {
super.set(key, super.has(key) ? super.get(key).concat(value) : [value]);
}
}
let foo = new Multimap([[21, 21], [0, 0], [90, 8], [1, 1], [60, 12], [90, 7], [75, 11], [55, 10], [90, 9], [54, 9]]);
foo.set(21, 22);
for (let [k,v] of foo.entries()) {
console.log(k, v);
}

How to sum elements at the same index in array of arrays into a single array?

Let's say that I have an array of arrays, like so:
[
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
How do I generate a new array that sums all of the values at each position of the inner arrays in javascript? In this case, the result would be: [17, 10, 19]. I need to be able to have a solution that works regardless of the length of the inner arrays. I think that this is possible using some combination of map and for-of, or possibly reduce, but I can't quite wrap my head around it. I've searched but can't find any examples that quite match this one.
You can use Array.prototype.reduce() in combination with Array.prototype.forEach().
var array = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
],
result = array.reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = (r[i] || 0) + b;
});
return r;
}, []);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Update, a shorter approach by taking a map for reducing the array.
var array = [[0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3]],
result = array.reduce((r, a) => a.map((b, i) => (r[i] || 0) + b), []);
console.log(result);
Using Lodash 4:
function sum_columns(data) {
return _.map(_.unzip(data), _.sum);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32]
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
For older Lodash versions and some remarks
Lodash 4 has changed the way _.unzipWith works, now the iteratee gets all the values passed as spread arguments at once, so we cant use the reducer style _.add anymore. With Lodash 3 the following example works just fine:
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32],
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
_.unzipWith will insert undefineds where the row is shorter than the others, and _.sum treats undefined values as 0. (as of Lodash 3)
If your input data can contain undefined and null items, and you want to treat those as 0, you can use this:
function sum_columns_safe(data) {
return _.map(_.unzip(data), _.sum);
}
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
console.log(sum_columns_safe([[undefined]])); // [0]
console.log(sum_columns([[undefined]])); // [undefined]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
This snipet works with Lodash 3, unfortunately I didn't find a nice way of treating undefined as 0 in Lodash 4, as now sum is changed so _.sum([undefined]) === undefined
One-liner in ES6, with map and reduce
var a = [ [0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3] ];
var sum = a[0].map((_, i) => a.reduce((p, _, j) => p + a[j][i], 0));
document.write(sum);
Assuming that the nested arrays will always have the same lengths, concat and reduce can be used.
function totalIt (arr) {
var lng = arr[0].length;
return [].concat.apply([],arr) //flatten the array
.reduce( function(arr, val, ind){ //loop over and create a new array
var i = ind%lng; //get the column
arr[i] = (arr[i] || 0) + val; //update total for column
return arr; //return the updated array
}, []); //the new array used by reduce
}
var arr = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
console.log(totalIt(arr)); //[17, 10, 19]
Assuming array is static as op showned.
a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
b = []
for(i = 0; i < a[0].length; i++){
count = 0
for(j = 0; j < a.length; j++){
count += a[j][i]
}
b.push(count)
}
console.log(b)
So far, no answer using the for ... of mentioned in the question.
I've used a conditional statement for different lengths of inner arrays.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
i = 0;
r = []
for (const inner of a) {
j = 0;
for (const num of inner) {
if (j == r.length) r.push(num)
else r[j] += num
j++;
}
i++;
}
console.log(r);
True, in this case, the classic for cycle fits better than for ... of.
The following snippet uses a conditional (ternary) operator.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
r = [];
for (i = 0; i < a.length; i++) {
for (j = 0; j < a[i].length; j++) {
j==r.length ? r.push(a[i][j]) : r[j]+=a[i][j]
}
}
console.log(r);
A solution using maps and reductions, adding elements from different lengths of arrays.
var array = [
[0],
[2, 4],
[5, 5, 7, 10, 20, 30],
[10, 0]
];
b = Array(array.reduce((a, b) => Math.max(a, b.length), 0)).fill(0);
result = array.reduce((r, a) => b.map((_, i) => (a[i] || 0) + (r[i] || 0)), []);
console.log(result);
const ar = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
ar.map( item => item.reduce( (memo, value)=> memo+= value, 0 ) )
//result-> [4, 12, 17, 13]

Categories

Resources