Complete array with nulls in javascript - 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)

Related

How to add items of arrays to end of array?

There is sourceArray and some additionalArray. Need to add items from additionalArray to the end of sourceArray. And in result sourceArray contains all items (no create new array). The problem is items count of additionalArray may be thousands.
// example
push([], [1, 2, 3], [10, 20, 30]) // [1, 2, 3, 10, 20, 30]
push(['a', 'b'], 'x', ['z', '0']) // ['a', 'b', 'x', 'z', '0']
// my solution
function push(sourceArray, ...additionalArray) {
additionalArray.forEach((array) => {
array = Array.isArray(array) ? array : [array];
if (array.length < 1000) {
sourceArray.push.apply(sourceArray, array);
} else {
array.forEach((item) => sourceArray.push(item));
}
});
return sourceArray;
}
My question is there more elegant solution for this task?
You might find using .flat() can help you here. If you use that on additionalArray, you can then spread ... those elements into a call to .push() as arguments:
const push = (source, ...rest) => {
source.push(...rest.flat());
return source;
}
console.log(push([], [1, 2, 3], [10, 20, 30])) // [1, 2, 3, 10, 20, 30]
console.log(push(['a', 'b'], 'x', ['z', '0'])) // ['a', 'b', 'x', 'z', '0']
This does have a limitation though in that .push() can only accept a certain amount of arguments. You might hit the max argument limit and this can throw considering that your additionalArray can be large. Using a for..of loop would help with that:
const push = (source, ...rest) => {
for(const item of rest.flat())
source.push(item)
return source;
}
console.log(push([], [1, 2, 3], [10, 20, 30])) // [1, 2, 3, 10, 20, 30]
console.log(push(['a', 'b'], 'x', ['z', '0'])) // ['a', 'b', 'x', 'z', '0']
As of MDN, you can use the array spread syntax:
let vegetables = ['parsnip', 'potato']
let moreVegs = ['celery', 'beetroot']
// Merge the second array into the first one
vegetables.push(...moreVegs);
console.log(vegetables) // ['parsnip', 'potato', 'celery', 'beetroot']

Rewriting lodash 'fill' function

I rewrited the fill function of lodash for the purpose of training JS.
Please, rewrite it better than me.
//////////////// fill(array, value, [start=0], [end=array.length]) ////////////////
Fills elements of array with value from start up to, but not including, end.
let array = [1, 2, 3];
function fill (arr, filler, start = 0, end = arguments[0].length) {
filler = new Array(end - start).fill(filler);
let temp = arr;
temp.splice(start,end === arguments[0].length ? end : end - 1, ...filler);
return temp
}
console.log(fill(array, 'a'));
// => ['a', 'a', 'a']
console.log(fill(Array(3), 2));
// => [2, 2, 2]
console.log(fill([4, 6, 8, 10], '*', 1, 3));
// => [4, '*', '*', 10]
console.log(fill([4, 6, 8, 10,12,14,18,20], '*', 1, 3));
// => [4, "*", "*", 10, 12, 14, 18, 20]
Is there a more elegant way to approach this?
The Array.fill() method is almost identical to lodash's _.fill(). The main difference is the way that it is applied to the array.
const fill = (arr, ...args) => arr.fill(...args);
console.log(fill([1, 2, 3], 'a')); // => ['a', 'a', 'a']
console.log(fill(Array(3), 2)); // => [2, 2, 2]
console.log(fill([4, 6, 8, 10], '*', 1, 3)); // => [4, '*', '*', 10]
console.log(fill([4, 6, 8, 10,12,14,18,20], '*', 1, 3)); // => [4, "*", "*", 10, 12, 14, 18, 20]

How to convert array to object in JavaScript

I want to convert the following array:
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
to the following object:
const data = [
{
a: 1,
b: 2,
c: 3,
d: 4
},
{
a: 5,
b: 6,
c: 7,
d: 8
},
{
a: 9,
b: 10,
c: 11,
d: 12
}
];
How do I do it using loop or some other way, of course?
This is a basic map / reduce operation where you map the outer array to a new one where each value (array) is reduced to a single object.
Given the simple nature of each value (a single array with four values), you can skip Array.prototype.reduce() for some array destructuring.
For example
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
const newData = data.map(([ a, b, c, d ]) => ({ a, b, c, d }))
console.info(newData)
Note that this really hinges on knowing the data structure and no surprises (more or fewer elements in each array for example).
If you wanted a much more robust version, you'll want something like this
const data = [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10, 11, 12]];
const newData = data.map(arr => arr.reduce((obj, val, idx) => ({
...obj,
[String.fromCharCode(97 + idx)]: val
}), Object.create(null)))
console.info(newData)
The only thing to worry about here is if your arrays contain more than 26 elements. Then your object keys are going to get weird.
You can use Array.prototype.map() combined with Array.prototype.reduce()
Code:
const alphabet = [...'abcdefghijklmnopqrstuvwxyz'];
const data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
const reasult = data.map(arr => arr.reduce((a, c, i) => (a[alphabet[i]] = c, a), {}));
console.log(reasult);

Get unique values from JSON with out looping

I am having a array in the format:
const resultData = [
[1, 2, 'a', 'b'],
[1, 4, 'c', 'f'],
[2, 5, 'a', 'b'],
[1, 2, 'c', 'd'],
[9, 3, 'c', 'f'],
[5, 4, 'f', 'g']
]
and I am trying to convert in in the format:
[{
value: "a,b",
data: [
[1, 2],
[2, 5]
]
}, {
value: "c,f",
data: [
[1, 4],
[9, 3]
]
}, {
value: "c,d",
data: [
[1, 2]
]
}, {
value: "f,g",
data: [
[5, 4]
]
}]
I am using a map currently with a for loop:
var mapp = new Map;
_.each(resultData, item => {
var x = item.col.slice(2);
if (mapp.has(x.toString())) {
var temp = mapp.get(x.toString());
temp.push([item.col[0], item.col[1]]);
mapp.set(x.toString(), temp);
} else {
var valuesArray = [];
valuesArray.push([item.col[0], item.col[1]])
mapp.set(x.toString(), valuesArray);
}
});
I am having a huge data set. Is there a possible way to do it without a loop or any other method?
You can make use of reduce and Object.values method like below
const resultData = [[1,2,'a','b'],[1,4,'c','f'],[2,5,'a','b'],[1,2,'c','d'],[9,3,'c','f'],[5,4,'f','g']]
const res = resultData.reduce((acc, item) => {
const key = item.slice(2).join();
const value = item.slice(0, 2);
if(!acc[key]) {
acc[key] = {value: key, data: [value]};
} else {
acc[key] = {...acc[key], data: [...acc[key].data, value]}
}
return acc;
}, {});
console.log(Object.values(res));
I would use reduce to generate the results:
check if the key values already exists
If it does, append the new items to the data array
If it does not, create the new object with the data
const resultData = [
[1, 2, 'a', 'b'],
[1, 4, 'c', 'f'],
[2, 5, 'a', 'b'],
[1, 2, 'c', 'd'],
[9, 3, 'c', 'f'],
[5, 4, 'f', 'g']
]
let result = resultData.reduce((arr, itm) => {
let value = itm[2] + ',' + itm[3]
let item = arr.find(i => i.value == value)
if (!item) arr.push({ value, data: [[itm[0], itm[1]]] })
else item.data.push([itm[0], itm[1]])
return arr
}, [])
console.log(result)
Javascript arrays have a .map method:
var newArray = resultData.map(function(arrayMember){
var objectToReturn = {};
objectToReturn['value'] = arrayMember; // add other transformations
return objectToReturn;
}); // map
another solution:
const res = _.chain(resultData)
.groupBy(item => _.takeRight(item, 2).join(','))
.mapValues((items, value) => ({
value,
data: _.map(items, arr => _.take(arr, 2))
}))
.values()
.value();

How to transpose an 2x2 array?

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))

Categories

Resources