How to transpose an 2x2 array? - javascript

I have an array in the form:
var a1 = [
['AA', 1],
['AA', 2],
['AA', 3],
['BB', 7],
['BB', 8],
['BB', 9]
];
I want to transform it into:
output = [
['AA':1,2,3],
['BB':7,8,9]
]
I need to transform it this way so I can put my JSON formatted data that comes from SQL into a highcharts graph that seems to need the array series as follows
https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/streamgraph/

Try something like this
var a1 = [
['AA', 1],
['AA', 2],
['AA', 3],
['BB', 7],
['BB', 8],
['BB', 9]
];
function generateObj(array) {
const obj = {}
array.forEach(entry => {
obj[entry[0]] = obj[entry[0]] || []
obj[entry[0]].push(entry[1])
})
return obj
}
console.log(generateObj(a1))

Related

get highest element in a two dimensional array

i have this array:
const myArray = [["Cow", 3], ["Pig", 5], ["Pig", 10], ["Pig", 4], ["Chicken", 1], ["Cow", 1], ["Cow" , 12], ["Cow", 11], ["Chicken", 12]]
and want to turn into this: (only the highest of each one)
[ [ 'Pig', 10 ], [ 'Cow', 12 ], [ 'Chicken', 12 ] ]
but with my code i cant get the last one, i cant find why tho
const myArray = [["Cow", 3], ["Pig", 5], ["Pig", 10], ["Pig", 4], ["Chicken", 1], ["Cow", 1], ["Cow" , 12], ["Cow", 11], ["Chicken", 12]]
function getHighest() {
var onlyHighest = [];
myArray.sort(
function(a,b) {
if (a[0] == b[0])
return a[1] < b[1] ? -1 : 1;
return a[0] < b[0] ? 1 : -1;
}
);
myArray.forEach((a, i) => {
var i = i+1;
if (i < myArray.length) {
if (a[0] != myArray[i][0]){
onlyHighest.push([a[0], a[1]]);
}
}
});
return console.log(onlyHighest)
// [ [ 'Pig', 10 ], [ 'Cow', 12 ] ]
}
Use an object to hold the current highest value for each animal. Loop through the array, replacing the value when it's higher than the value in the object.
const myArray = [["Cow", 3], ["Pig", 5], ["Pig", 10], ["Pig", 4], ["Chicken", 1], ["Cow", 1], ["Cow" , 12], ["Cow", 11], ["Chicken", 12]]
function getHighest(array) {
let obj = {};
array.forEach(([key, value]) => {
if (key in obj) {
if (value > obj[key]) {
obj[key] = value;
}
} else {
obj[key] = value;
}
});
return Object.entries(obj);
}
console.log(getHighest(myArray));
Your
myArray.forEach((a, i) => {
var i = i+1;
if (i < myArray.length) {
is causing the problem and is pretty confusing. If the last sorted chunk of the array contains only 2 elements, that condition won't be fulfilled, so nothing from that chunk will be pushed.
A quick fix would be to unconditionally push the original element if it's the last one in the array.
myArray.forEach((a, i) => {
if (i === myArray.length - 1) {
A better refactor would be to group the input into an object that keeps the largest value for each property, no sorting involved.
const myArray = [["Cow", 3], ["Pig", 5], ["Pig", 10], ["Pig", 4], ["Chicken", 1], ["Cow", 1], ["Cow" , 12], ["Cow", 11], ["Chicken", 12]]
const grouped = {};
for (const [prop, num] of myArray) {
grouped[prop] = Math.max(num, grouped[prop] ?? -Infinity);
}
const onlyHighest = Object.entries(grouped);
console.log(onlyHighest);
How about a simple sort
function funk() {
let a = [["Cow", 3], ["Pig", 5], ["Pig", 10], ["Pig", 4], ["Chicken", 1], ["Cow", 1], ["Cow", 12], ["Cow", 11], ["Chicken", 12]];
let r = a.sort((a, b) => {
return b[1] - a[1]
})[0];
Logger.log(r);
}
Execution log
12:37:11 PM Notice Execution started
12:37:11 PM Info [Cow, 12.0]
12:37:12 PM Notice Execution completed

Complete array with nulls in javascript

This is a similar question as this one: Complete missing sequence in array with zeros in javascript
However, I can't seem to go around this problem. This is my array:
const array = [
[5, 'a', 2.3],
[6, 'a', 1.7],
[7, 'a', 5.4],
[8, 'a', 2.8],
[9, 'a', 8.5],
[10, 'a', 9.2],
[2, 'b', 1.6],
[5, 'b', 5.7],
[6, 'b', 8.9],
[7, 'b', 3.5],
[8, 'b', 6.1],
[9, 'b', 1.8],
[10, 'b', 7.4],
];
console.log(array);
First element: this is my reference value, it ranges from 1 to 10.
Second element: this is a category value.
Third element: this is a value that belongs to the second element, which happened at a timestamp that belongs to the first element.
My issue: I need to make sure that all the unique categories in the second element of the array (e.g., a and b) have the following sequence in the first element: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. If they do not have one of these numbers, then I need to create it, and then assign null to the third element.
Therefore, this is my expected output:
[
[1, 'a', null],
[2, 'a', null],
[3, 'a', null],
[4, 'a', null],
[5, 'a', 2.3],
[6, 'a', 1.7],
[7, 'a', 5.4],
[8, 'a', 2.8],
[9, 'a', 8.5],
[10, 'a', 9.2],
[1, 'b', null],
[2, 'b', 1.6],
[3, 'b', null],
[4, 'b', null],
[5, 'b', 5.7],
[6, 'b', 8.9],
[7, 'b', 3.5],
[8, 'b', 6.1],
[9, 'b', 1.8],
[10, 'b', 7.4],
];
Any ideas?
You can create a range from 1 to 10, loop over it and when you can't find an association in your array, create a new element and push it.
Do that for every category and you're good.
const range = new Array(0).fill().map((_, i) => i + 1); // from 1 to 10
const categories = array
.map(x => x[1]) // get categories
.filter((val, i, self) => self.indexOf(val) === i) // uniq
categories.forEach(categ => {
range.forEach(n => {
const alreadyInArray = array.some(x => x[0] === n && x[1] === categ);
if (!alreadyInArray) {
const newEntry = [n, categ, null];
array.push(newEntry);
}
});
})
You can of course replace the forEach with classic for loops
A functional solution, first get the categories, then for each category fill the corresponding array.
const array = [
[5, 'a', 2.3],
[6, 'a', 1.7],
[7, 'a', 5.4],
[8, 'a', 2.8],
[9, 'a', 8.5],
[10, 'a', 9.2],
[2, 'b', 1.6],
[5, 'b', 5.7],
[6, 'b', 8.9],
[7, 'b', 3.5],
[8, 'b', 6.1],
[9, 'b', 1.8],
[10, 'b', 7.4],
];
const getMissingIndicesFromCategory = (tuples) => {
const indices = tuples.map(tuple => tuple[0])
const fullIndices = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
return fullIndices.filter(index => !indices.includes(index));
}
const createMissingTuples = (missingIndices, category) => {
return missingIndices.map(index => [index, category, null])
}
const completeCategoryTuples = (array, category) => {
const categoryTuples = array.filter(tuple => tuple[1] === category)
const missingIndices = getMissingIndicesFromCategory(categoryTuples)
const missingTuples = createMissingTuples(missingIndices, category)
return [...categoryTuples, ...missingTuples].sort((tuple1, tuple2) => tuple1[0] > tuple2[0] ? 1 : -1)
}
const getAllUniqueCategories = (array) => Array.from(new Set(array.map(tuple => tuple[1])))
const fillArray = (array) => {
const categories = getAllUniqueCategories(array)
return categories.flatMap(category => completeCategoryTuples(array, category))
}
const fullArray = fillArray(array)
console.log(fullArray)

Extract child arrays from nested arrays

I have nested array data and I would like to extract all nested arrays to be siblings of their parent. I am pretty close, but I am getting an extra empty array in the results and I cannot figure out where it is coming from or how to get rid of it.
Note: I would really like to understand why this is happening and how to get rid of it in my function, and not just a .filter(arr => arr.length) on my results list.
This is my attempt so far:
var arrs = [
[1, 2, [3, 4], 5],
[6, [7, 8, 9, [10, 11]]],
[12, 13],
[[14, 15], [16, 17]],
[[1], 4, [1, 1], 4]
];
// Desired Output
// [
// [1, 2, 5],
// [3, 4],
// [6],
// [7, 8, 9],
// [10, 11],
// [12, 13],
// [14, 15],
// [16, 17],
// [4, 4]
// [1]
// [1, 1]
// ]
function extractArrays (arr) {
return arr.reduce((res, curr) => {
if (Array.isArray(curr)) {
res = res.concat(extractArrays(curr));
}
else {
res[0].push(curr);
}
return res;
}, [[]]);
}
console.log(extractArrays(arrs));
// Results:
// [
// [], <-- Where is this coming from?
// [ 1, 2, 5 ],
// [ 3, 4 ],
// [ 6 ],
// [ 7, 8, 9 ],
// [ 10, 11 ],
// [ 12, 13 ],
// [], <-- Also here
// [ 14, 15 ],
// [ 16, 17 ],
// [ 4, 4 ],
// [ 1 ],
// [ 1, 1 ]
// ]
.as-console-wrapper {
max-height: 100% !important;
}
Element like [[14, 15], [16, 17]] will introduce a [] after recursion. This should be handled by checking length.
var arrs = [
[1, 2, [3, 4], 5],
[6, [7, 8, 9, [10, 11]]],
[12, 13],
[[14, 15], [16, 17]],
[[1], 4, [1, 1], 4]
];
function extractArrays (arr, acc=[]) {
if (arr.length == 0 ) return acc;
let pure = arr.filter(elm => !Array.isArray(elm));
if (pure.length > 0) {
acc.push(pure);
}
acc.concat(arr.filter(elm => Array.isArray(elm)).map(elm => extractArrays(elm, acc)));
return acc;
}
console.log(extractArrays(arrs));
You can try the following code
var arrs = [
[1, 2, [3, 4], 5],
[6, [7, 8, 9, [10, 11]]],
[12, 13],
[
[14, 15],
[16, 17]
], // <-- added additional test case
[
[1], 4, [1, 1], 4
]
];
function extractArrays(arr) {
return arr.reduce((res, curr, i) => {
if (Array.isArray(curr)) {
res = res.concat(extractArrays(curr));
} else {
let index = 0;
for (let j = 0; j <= i; j++) {
if (!Array.isArray(arr[j])) {
res[index] ? res[index].push(curr) : res.push([curr]);
break;
} else {
index++;
}
}
}
return res;
}, []); // <-- no initial empty array inside here
}
console.log(extractArrays(arrs));
I just wanted to share my approach to this problem, I enjoyed trying to solve it, in my case I also passed an array to the extractArrays method, in order to make easier to capture and filter every array inside the arrs param.
let result = [];
extractArrays(arrs, result);
console.log(result);
function extractArrays(arr, result) {
let newResult = arr.reduce((acc, curr) => {
if (Array.isArray(curr)) {
extractArrays(curr, result);
} else {
acc.push(curr);
}
return acc;
}, []);
newResult.length && result.push(newResult);
}
You can check it when you return from function. stackblitz
function extractArray(arr) {
const res = arr.reduce((res, curr) => {
if(!Array.isArray(curr)){
return [[...res[0], curr], ...res.slice(1)]
}
return [...res, ...extractArray(curr)]
}, [[]]);
return res[0].length ? res : res.slice(1);
}
EDIT: More performant function (check stackblitz link)
function extractFaster(arr) {
let res = [0];
function recExtract(arr) {
let hasNonArrayElm = false;
let index = res.length -1;
arr.forEach(curr => {
if (!Array.isArray(curr)) {
hasNonArrayElm ? res[index].push(curr) : res.splice(index, 0, [curr]);
hasNonArrayElm = true;
return;
}
recExtract(curr);
});
}
recExtract(arr);
res.splice(-1, 1)
return res;
}
EDIT: The answer below the line is a great way to flatten arrays, but I suggested it because I misunderstood this question. I will leave it in case it benefits someone to know, but in order to keep an accurate record, I'll also update my answer to address the problem posed in the question.
The accepted answer seems sufficient enough, but I'll try my hand at it. I would use Array.reduce to cover everything with one swoop, and inside use Array.filter to separate the normal items from the array items, then use the spread ... operator on the nested arrays so everything gets pushed to the same level, after recursively calling the same extract function on all nested arrays. Honestly, the explanation might be harder to understand than the code, have a look:
const data = [
[1, 2, [3, 4], 5],
[6, [7, 8, 9, [10, 11]]],
[12, 13],
[[14, 15], [16, 17]],
[[1], 4, [1, 1], 4]
]
const extractChildArrays = arrs => arrs.reduce((acc, cur) => {
const nestedArrs = cur.filter(a => Array.isArray(a))
const normalItems = cur.filter(a => !Array.isArray(a))
acc.push(normalItems, ...extractChildArrays(nestedArrs))
return acc
}, [])
console.log(extractChildArrays(data))
UPDATE: Array.flat() is now accepted as part of the spec and it's supported by all modern browsers except Edge.
In ES6 there is actually an experimental array method called flat(). As of the writing of this answer, it's only compatible with Chrome, but a polyfill might be worth looking into, because it's SO EASY!
The first parameter of flat() is depth, so with the help of another answer, you can easily figure that out dynamically.
const data = arrs = [
[1, 2, [3, 4], 5],
[6, [7, 8, 9, [10, 11]]],
[12, 13],
[[14, 15], [16, 17]],
[[1], 4, [1, 1], 4]
]
const flattenArray = arr => {
const getDepth = a => Array.isArray(a) ?
1 + Math.max(...a.map(getDepth)) : 0
return arr.flat(getDepth(arr))
}
console.log(flattenArray(data))

Adding two-dimensional arrays in angular/javascript

I am learning basic javascript here, please help, working on adding two-dimensional arrays, where 0th index will be always date and 1st index will be always integer in array, what i am trying to achieve is produce a new array with unique objects type=A and type=B by adding its 1st index of two-dimensional arrays. see below for example code
$scope.mainArray = [{
type: 'A',
values: [
[111111, 12],
[111111, 12],
[111111, 11],
[111111, 2],
]
}, {
type: 'B',
values: [
[111111, 12],
[111111, 12],
[111111, 11],
[111111, 2],
]
}, {
type: 'A',
values: [
[111111, 12],
[111111, 12],
[111111, 11],
[111111, 2],
]
}, {
type: 'B',
values: [
[111111, 12],
[111111, 12],
[111111, 11],
[111111, 2],
]
}, ];
Should convert to
$scope.newArray = [{
type:'A',
values:[
[11111,24],
[11111,24],
[11111,22],
[11111,4],
]
},{
type:'B',
values:[
[11111,24],
[11111,24],
[11111,22],
[11111,4],
]
}];
Plunker link
Any help is much appreciated.
I see that you updated the snippet so here is an example of using map/filter/reduce to get the output you're looking for.
$scope.typeAArray = $scope.mainArray.filter(function (obj) {
return obj.type === 'A';
}).reduce(function (a, b) {
var newObj = {
type: a.type,
values: []
};
a.values.map(function (v, index) {
newObj.values.push(v);
newObj.values[index][1] += b.values[index][1];
});
return newObj;
});

Get some set of array based on condition from two arrays of array in Javascript

I have two arrays of array in Javascript like
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]];
var array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]];
I want to get the set of arrays from array1 which match the first element of each array of the array2
in my example case both array1 and array2 have array with first element as 10 11 and 12, so it should return
[[10, 2], [11, 4], [12, 30]];
is there any easy and efficient way using pure javscript or lodash, underscor framework or something like that. Without iterate over and match one by one of this two array ?
In ES6, you could use Set.
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
set = new Set(array2.map(a => a[0])),
result = array1.filter(a => set.has(a[0]));
console.log(result);
Version with an object as hash table
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
result = array1.filter(function (a) {
return this[a[0]];
}, array2.reduce(function (r, a) {
r[a[0]] = true;
return r;
}, Object.create(null)));
console.log(result);
You can use lodash _.intersectWith function in order to solve this problem in an inline.
_.intersectionWith(array1, array2, function(a, b) {
return a[0] === b[0];
});
I don't know about performance cos I haven't had the chance to have a look at the source code of this function. Anyway, I like it for its simplicity. Here's the fiddle in case you want to check it out.
If you can make use of Set, then you can compute the a set of numbers to look for first and use .filter to only get the arrays whose first element is in that set:
var haystack = new Set(array2.map(x => x[0]));
var newArray = array1.filter(x => haystack.has(x[0]));
Of course you can also use the lodash or underscore versions of .map and .filter.
Alternatives to using Set would be:
Create an array of numbers instead and use indexOf to test existence. That will scale linearly with the number of elements:
var haystack = array2.map(x => x[0]);
var newArray = array1.filter(x => haystack.indexOf(x[0]) > -1);
Create an object with number -> true entries to test existence with in, hasOwnProperty or just object access:
var haystack = array2.reduce((obj, x) => (obj[x[0]] = true, obj), {});
var newArray = array1.filter(x => haystack[x[0]]);
Which one performs better depends on the number of elements you have and the environment the code is running in.
You can do this with filter() and find()
var array1 = [
[10, 2],
[11, 4],
[12, 30],
[13, 17],
[14, 28]
];
var array2 = [
[8, 13],
[9, 19],
[10, 6],
[11, 7],
[12, 1]
];
var result = array1.filter(function(ar) {
return array2.find(function(e) {
return e[0] == ar[0]
})
})
console.log(result)
I would do this with a Map anf filter combo in ES6 as follows;
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
m = new Map(array2),
array3 = array1.filter(a => m.has(a[0]));
console.log(array3);
If you need backwards compatibility other answers are pretty good.

Categories

Resources