How to make an average of nested array - javascript

I'm trying to make an average array of a bigger and dynamic array. Simpler looks like this:
const bigArr = [[[1,1,1], [2,2,2], [3,3,3]],[[3,3,3], [4,4,4], [7,7,7]]]
in the end, I'm expecting to get:
const averageArray = [[2,2,2], [3,3,3], [5,5,5]]
I know the best way is to triple loop over this array's, but I couldn't manage to get expected result.
averageArray[0][0] is an average of bigArr[0][0] and bigArr[1][0].

There are a few ways to do it (for loops, reduce, etc.) here I show an example with reduce:
const bigArr = [
[[1,1,1], [2,2,2], [3,3,3]],
[[3,3,3], [4,4,4], [7,7,7]],
//[[5,5,5], [6,6,6], [11,11,11]]
];
const averageArray = bigArr.reduce((aggArr, arr, i) => {
if (i == 0){
return arr.map( a => a );
}
else {
arr.forEach( (a, j) => {
a.forEach( (b, k) => {
aggArr[j][k] = ((aggArr[j][k] * i) + b) / (i + 1)
})
});
}
return aggArr;
}, []);
console.log(averageArray);
Output:
[[2,2,2], [3,3,3], [5,5,5]]
It would also work with a larger input like this:
const bigArr = [
[[1,1,1], [2,2,2], [3,3,3]],
[[3,3,3], [4,4,4], [7,7,7]],
[[5,5,5], [6,6,6], [11,11,11]]
];
We get this output:
[[3,3,3], [4,4,4], [7,7,7]]
One final example:
It would also work with a larger input with non identical sub-arrays like this (to illustrate how the averaging is occurring):
const bigArr = [
[[1,2,3], [1,2,3], [1,2,3]],
[[3,4,7], [3,4,7], [3,4,7]],
[[5,6,11], [5,6,11], [5,6,11]]
];
We get this output:
[[3,4,7], [3,4,7], [3,4,7]]

Related

Group table data into groups of N in Typescript [duplicate]

Let's say I have this array of characters I want to use:
var acceptableCharacters = ['a','b','c','d'];
I'd like to get all possible combinations of this array, say from a length of 2 to 4, so the output might look something like this
aa
ab
ac
ad
ba
bb
bc
bd
ca
cb
cc
cd
da
db
dc
dd
...And so on...
The generator should be able to meet these conditions:
Aims for all combinations between 2 lengths, or allows itself to accept any different length
Letters can repeat themself
I'm not really sure where to start, so some starter code would be nice.
edit:
var characters = ['a','b','c','d'];
var combinations = [];
for(var i=2;i<=4;i++) {
var str = "";
for(var c of characters) {
str+=c;
// 'a' spam
}
combinations.push(str);
};
console.log( combinations );
I don't know how to iterate the characters ¯_(ツ)_/¯
It looks like this:
ab
abc
abcd
The issue I see with your code is that you need a nested loop for each additional combination length, i.e. for length 2 you need a double-nested iteration, for length 3 a triple-nested iteration, and so on.
Took a stab at an implementation using recursion. It recurses down to the base case of length 1 and builds up the result set.
const characters = ["a", "b", "c", "d"];
const combinations = (arr, min = 1, max) => {
const combination = (arr, depth) => {
if (depth === 1) {
return arr;
} else {
const result = combination(arr, depth - 1).flatMap((val) =>
arr.map((char) => val + char)
);
return arr.concat(result);
}
};
return combination(arr, max).filter((val) => val.length >= min);
};
const result = combinations(characters, 2, 4);
console.log(`Combinations: ${result.length}`, result);
Here's a non-recursive version; it ended up being a bit simpler.
const characters = ["a", "b", "c", "d"];
const combinations = (arr, min = 1, max) =>
[...Array(max).keys()]
.reduce(
(result) =>
arr.concat(result.flatMap((val) => arr.map((char) => val + char))),
[]
)
.filter((val) => val.length >= min);
const result = combinations(characters, 2, 4);
console.log(`Combinations: ${result.length}`, result);

How do i move through the values of keys in javascript?

How would i cycle move the key values in a js array,
for example:
{Name:"A", Task:"Task1"},
{Name:"B", Task:"Task2"},
{Name:"C", Task:"Task3"},
to
{Name:"A", Task:"Task3"},
{Name:"B", Task:"Task1"},
{Name:"C", Task:"Task2"},
to clarify further it should be a function that every time is run "shifts the task column" by one.
I have tried using methods such as:
ary.push(ary.shift());
however i don't know any way that i can specifically apply this to a specific key while not moving the others.
Map the array, and take the task from the previous item in the cycle using the modulo operation.
Unlike the % operator the result of using modulo with negative numbers would be a positive remainder. It's needed because in the first item (i is 0) i - 1 would be -1.
const modulo = (a, n) => ((a % n ) + n) % n
const fn = arr =>
arr.map((o, i) => ({
...o,
Task: arr[modulo((i - 1), arr.length)].Task
}))
const arr = [{"Name":"A","Task":"Task1"},{"Name":"B","Task":"Task2"},{"Name":"C","Task":"Task3"}]
const result = fn(arr)
console.log(result)
You are very close. You can decompose the array into two individual ones and shift one of them. See below function:
const arr = [
{ Name: "A", Task: "Task1" },
{ Name: "B", Task: "Task2" },
{ Name: "C", Task: "Task3" }
];
function cycle(arr) {
const names = arr.map(el => el.Name);
const tasks = arr.map(el => el.Task);
const el = tasks.pop();
tasks.unshift(el);
return names.map((letter, i) => {
return {
Name: letter,
Task: tasks[i]
};
});
}
console.log(cycle(arr));
Also, codepen for conveninence.
Approach using a Generator and your pop and shift concept
const data=[{Name:"A",Task:"Task1"},{Name:"B",Task:"Task2"},{Name:"C",Task:"Task3"}];
function* taskGen(data){
// create standalone array of the names that can be shifted
const tasks = data.map(({Task}) => Task);
while(true){
// move first to last position
tasks.push(tasks.shift());
// return new mapped array
yield data.map((o, i) => ({...o, Task: tasks[i] }))
}
}
const shifter = taskGen(data);
for (let i =0; i< 6; i++){
const next = shifter.next().value
console.log(JSON.stringify(next))
}
.as-console-wrapper {max-height: 100%!important;top:0;}

How to get "unfiltered" array items?

Let's say I have an array which I filter by calling myItems.filter(filterFunction1) and get some items from it.
Then I want to run another filtering function filterFunction2 against the remaining items which were not selected by filterFunction1.
Is that possible to get the remaining items that were left out after calling a filtering function?
You'd have to rerun the filter with an inverted predicate, which seems wasteful. You should reduce the items instead and bin them into one of two bins:
const result = arr.reduce((res, item) => {
res[predicate(item) ? 'a' : 'b'].push(item);
return res;
}, { a: [], b: [] });
predicate here is the callback you'd give to filter.
Unfortunately, there is no one-step solution based on filter. Still the solution is a simple one-liner:
Here's an example
const arr = [ 1,2,3,4,5,6,7,8 ];
const filtered = arr.filter(x=>!!(x%2))
const remaining = arr.filter(x=>!filtered.includes(x))
console.log(filtered, remaining);
You could map an array of flags and then filter by the flags values.
const cond = v => !(v % 2);
var array = [1, 2, 3, 4, 5],
flags = array.map(cond),
result1 = array.filter((_, i) => flags[i]),
result2 = array.filter((_, i) => !flags[i]);
console.log(result1);
console.log(result2);
You can achieve that using Array.reduce.
const myItems = [...];
const { result1, result2 } = myItems.reduce(
(result, item) => {
if (filterFunc1(item)) {
result.result1.push(item);
} else if (filterFunc2(item)) {
result.result2.push(item);
}
return result;
},
{ result1: [], result2: [] },
);
If you don't want to use reduce, you may want to iterate the array once and acquire the filtered and unfiltered items in a single shot, using a plain efficient for..of loop:
function filterAndDiscriminate(arr, filterCallback) {
const res = [[],[]];
for (var item of arr) {
res[~~filterCallback(item)].push(item);
}
return res;
}
const [unfiltered, filtered] = filterAndDiscriminate([1,2,3,4,5], i => i <= 3);
console.log(filtered, unfiltered);
There's a way more simple and readable way to do this:
const array1 = []
const array2 = []
itemsToFilter.forEach(item => item.condition === met ? array1.push(challenge) : array2.push(challenge))

Sorting an Array of comma separated string using JavaScript

I came across with a weird requirement and I am struggling for last few hours to complete it. Below is my Array of string(just an example, the actual array contains around 2500 records):
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
We have 3 element here of which each element is comma separated(each element have 6 item). i.e:
testArray[0] = "130,839.9,855,837.3,848.65,3980489"
My problem is, I wanted to sort testArray based on the first item of each element and convert it to array of array having all value into float, so the output would be:
[
[129, 875, 875, 828.1, 833.25, 6926078],
[130, 839.9, 855, 837.3, 848.65, 3980489],
[138, 891.3, 893.3, 865.2, 868.75, 5035618]
]
I am able to sort individual item but not the entire array as a whole, and I have tried using split and then sort with no luck.
Can someone help me out with this and please let me know if I am not clear.
Convert the array using Array#map within an Array#map, then use Array#sort on the converted array according to the [0] indices (a[0] - b[0]):
In ES5
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
var converted = testArray.map(function (item) {
return item.split(',').map(function (num) {
return parseFloat(num);
});
})
console.log(converted)
var sorted = converted.sort(function (a, b) { return a[0] - b[0] })
console.log(sorted)
In ES6
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const converted = testArray.map(
item => item.split(',').map(
num => parseFloat(num)
)
)
console.log(converted)
const sorted = converted.sort((a, b) => a[0] - b[0])
console.log(sorted)
In ES6 (condensed)
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const convertedAndSorted = testArray
.map(n => n.split(',')
.map(num => parseFloat(num)))
.sort((a, b) => a[0] - b[0])
console.log(convertedAndSorted)
Just map the splitted and to number formatted values and sort by the first item.
var data = ["130,839.9,855,837.3,848.65,3980489", "129,875,875,828.1,833.25,6926078", "138,891.3,893.3,865.2,868.75,5035618"],
result = data
.map(s => s.split(',').map(Number))
.sort((a, b) => a[0] - b[0]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var testArray = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
const output = [];
for (let i = 0; i < testArray.length; i++) {
var numbers = testArray[i].split(',');
for (let j = 0; j < numbers.length; j++) {
numbers[j] = +numbers[j];
}
output[i] = numbers;
}
output.sort(function(x, y) {
return x[0] - y[0];
});
or shorter
output = testArray.map(s => s.split(',')).map(e => e.map(n => +n)).sort((x, y) => x[0] - y[0]);
First convert each of the Strings to an array of floats values using Array.map() and parseFloat().
After that you can simply sort the array of arrays using Arrays.sort()
Try the following :
var arr = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
var result = arr.map((a)=> a.split(",").map((b)=>parseFloat(b))).sort((a,b)=> a[0] -b[0]);
console.log(result);

Reduce array list, to set of common paths

I am trying to reduce a list to a shorter list, of just common filesystem paths. Trying to find all the common grandparents, and put only those in the final list. Here is the goal: The goal is that we must eliminate all directories in the list for which there is a parent directory in the list.
A better way to phrase that might be:
The goal is that we must eliminate all paths in the list for which there is a parent directory of that path in the list.
Say I have this input and expected output:
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
const output = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
];
const getReducedList = function (input) {
return input
.sort((a, b) => (a.length - b.length))
.reduce((a, b) => {
// console.log('a:', a, 'b:', b);
const s = !a.some(v => {
return b.startsWith(v);
});
if (s) {
a.push(b);
}
return a;
}, []);
};
console.log(getReducedList(input));
that getReducedList function seems to work with our first test case, 5 is reduced to 2. But, if we add a second test case, here is where things get weird:
If I remove an item from the original list and change it to this list of 4:
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
then I would expect to get this output (the same list of 4):
const output = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
the reason why I would expect/desire the same list of 4, is because no item in the list has a parent directory elsewhere in the list. But I actually get this output, a list of 2, which is incorrect:
const output = [
'/home/oleg/WebstormProjects/oresoftware/r2g',
'/home/oleg/WebstormProjects/oresoftware/sumanjs/suman'
];
does anyone know how I can fix this to get the expected result instead? An answer needs to satisfy both test cases.
To make it perfectly clear, if you add "/home/oleg" to the original list, then "/home/oleg" should be the only entry in the output.
I think you can do this with a very simple recursive function. You simply sort by length, then recursively pop the shortest, add it to the results, filter the array with that, recurse:
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
input.sort((a,b) => b.length - a.length)
function getPrefixes(list, res =[]) {
if (list.length < 1) return res
let next = list.pop()
res.push(next)
return getPrefixes(list.filter(u => !u.startsWith(next + '/')), res)
}
console.log(getPrefixes(input))
Looks like we only needed to change one line. Here is the original:
const getReducedList = function (input) {
return input
.sort((a, b) => (a.length - b.length))
.reduce((a, b) => {
const s = !a.some(v => {
return b.startsWith(v);
});
if (s) {
a.push(b);
}
return a;
}, []);
};
we need to change the one line to this instead:
return b.startsWith(v + '/');
You could simply use node's path module
const path = require('path');
const input = [
'/home/oleg/WebstormProjects/oresoftware/r2g',
'/home/oleg/WebstormProjects/oresoftware/sumanjs/suman',
'/home/oleg/WebstormProjects/oresoftware/sumanjs',
'/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types',
'/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch'
];
const s = new Set();
const out = [];
input.forEach(e => {
let p = path.dirname(e);
if (! s.has(p)) {
s.add(p);
out.push(e);
}
});
console.log(out);
The forEach could obviously replaced by reduce if you want to...
If you can use sets from es6 then you can simply sort with respect to directory length and keep pushing to a set.
You could add a slash for look up and pop the last value if found in next element.
This proposal uses a sorted list by value, not by lenght of the string, because it needs a sorted list for compairing the next element.
function uniqueFolder(array) {
return array
.sort()
.reduce((a, b) => {
if (b.startsWith(a[a.length - 1] + '/')) {
a.pop();
}
a.push(b);
return a;
}, []);
}
function commonPath(array) {
return array
.sort()
.reduce((a, b) => {
if (!b.includes(a[a.length - 1])) {
a.push(b);
}
return a;
}, []);
}
const input = [ "/home/oleg/WebstormProjects/oresoftware/r2g", "/home/oleg/WebstormProjects/oresoftware/sumanjs/suman", "/home/oleg/WebstormProjects/oresoftware/sumanjs", "/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types", "/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"];
console.log(uniqueFolder(input));
console.log(commonPath(input));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You need to avoid matches when the next value does not have its last forward slash following the previous value.
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
const getReducedList = (set => input => input.filter(b =>
!set.has(b.substr(0, b.lastIndexOf("/")))))(new Set(input));
console.log(getReducedList(input));
The logic is that it puts all paths in a set, and then checks for each path whether its parent is in the set. A path's parent is the path that has all characters up to, and excluding, the final /.
Removing the second entry from the input will not change the output. suman is treated the same way as suman-types and suman-watch.
Here is a more verbose, ES5 version (replacing the Set with a plain object):
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
function getReducedList(input) {
var hash = {};
input.forEach(function(path) {
hash[path] = true;
});
return input.filter(function (b) {
return !hash[b.substr(0, b.lastIndexOf("/"))];
}, []);
}
console.log(getReducedList(input));
It works without sort though has other drawbacks.
Edit: Had to add sort to work properly
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
const input2 = [
"/home/oleg/WebstormProjects/oresoftware",
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch"
];
function getParents(input) {
return input.sort((a, b) => a.length - b.length)
.reduce((acc, a) => {
const index = acc.findIndex(b => b.startsWith(a) || a.startsWith(b));
const slashDiff = index > 0 ? a.split('/').length === acc[index].split('/').length : false;
if (index === -1 || slashDiff) {
return [...acc, a];
}
acc.splice(index, 1, a.length < acc[index].length ? a : acc[index])
return acc;
}, []);
}
console.log(getParents(input));
console.log(getParents(input2));
const input = [
"/home/oleg/WebstormProjects/oresoftware/r2g",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman",
"/home/oleg/WebstormProjects/oresoftware/sumanjs",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-watch",
"/home/oleg/WebstormProjects/oresoftware/sumanjs/suman-types-blabla",
];
let ouput = input.sort().reduce((a, c)=> {
let d = true;
for (let j =0; j< a.length; j++){
if(c.startsWith(a[j]+'/')) {
d=false;
break;}
}
if (d) a.push(c);
return a;
},[]);
console.log(ouput);

Categories