JS Javascript - How to put array values in another array by indexes? - javascript

I have this array/object:
const template = {0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 6]}
// I have the above but happy to convert the below if it helps:
//const templateArr = [[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 6]]
Then I have an array that I want to map into that sequence:
const myArray = ["Q","W","A","S","Z","X,"E","R","D"] // for example
My intention it to have following result:
let myResult = [["Q", "W", "A", "S", "Z", "X"],["Q", "W", "A", "S", "Z", "E"]]
So all the values of myArray are in the positions set by template.
I'm not sure if I should use .map() or something else... Can someone point me in the right direction??
Thank you so much!

Yes, .map() is the right tool here. You can use two, one outer one to map over your inner arrays from templateArr, and then an inner map to map over the numbers (indexes) in your inner arrays, which will transform (ie: map) each number to the corresponding value at the index from myArray:
const templateArr = [[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 6]];
const myArray = ["Q","W","A","S","Z","X","E","R","D"];
const res = templateArr.map(inner => inner.map(idx => myArray[idx]));
console.log(JSON.stringify(res)); // JSON.stringify to pretty-print

Object.values will convert your template into array of arrays, then simply do map over it and another inner map over indicies and replace
them with particular letter from myArray.
Code:
const template = {0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 6]}
const myArray = ["Q","W","A","S","Z","X","E","R","D"]
const res = Object.values(template)
.map(a => a.map(idx => myArray[idx] ?? null))
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore this */

I think this should work:
const res = templateArr.map( (arr) => {
return arr.map( (index) => {
return myArray[index]
})
})

Related

How to project a key into an array to produce an array of objects?

Given this source object:
const src = { a: [1, 2, 3]} }
I want to transform it to an array of objects representing all combinations(3):
[{a: 1}, {a: 2}, {a: 3}]
The code I currently have works, but feels inelegant:
const arrPairs = [];
Object.keys(src).map((el) => {
src[el].forEach(element => {
arrPairs.push({[el]: element});
});
});
I have a feeling that some combination of map/reduce would do this far cleaner I just can't figure it out.
You can iterate over the entries with .flatMap and map each value to its associated object.
const src = { a: [1, 2, 3],
b: [4, 5, 6, 7],
c: [8, 9, 10, 11, 12] };
const result = Object.entries(src)
.flatMap(([key, arr]) => arr.map(val => ({[key]: val})));
console.log(result);
You can easily achieve the result with reduce and map
const src = {
a: [1, 2, 3],
};
const result = Object.keys(src).reduce(
(acc, curr) => [...acc, ...src[curr].map((val) => ({ [curr]: val }))],
[]
);
console.log(result);

Create a sorted array by mistake using .map()

I tried to make something that works as Set() using a couple tools that I learned. It worked, but I noticed a bug: it sorted my array! Can explain me someone why, please?
Here's my code:
function uniteUnique(...arr) {
let array = arr.flat()
let newArr = [];
console.log(array) // [ 1, 3, 2, 5, 2, 1, 4, 2, 1 ]
let myMap = array
.map(elem => {
if (!newArr.includes(elem))
return newArr.push(elem)
})
.filter(Boolean)
console.log(myMap) // [ 1, 2, 3, 4, 5 ]
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
I know that for you might be too simple, but I ask so I can understand what is happening here.
console.log(myMap) // [ 1, 2, 3, 4, 5 ]
The result of your log is the number of pushed elements but accidentally, you thought they are sorted.
Also, if you return the mapped list you will end up with an array that contains an integer and boolean values. Instead of this, you need to return newArr.
Your code will be like this :
function uniteUnique(...arr) {
let flattedArray = arr.flat()
let set = [];
flattedArray.forEach(elem => !set.includes(elem) && set.push(elem))
return set
}
const set = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(set)
in your code MyMap holds your newArr length as array.push returns the length of your array
so every time it returns the count:
for example if you tried to run this code
let newArr = []
console.log(newArr.push(20)) // the output is 1
and that's what your myMap holds => the length of your newArr
so if you want the filtered array you should use newArr
let array = [ 1, 3, 2, 5, 2, 1, 4, 2, 1 ]
let newArr = [];
let myMap = array.map(elem => {
if (!newArr.includes(elem))
return newArr.push(elem)
}).filter(Boolean)
console.log(newArr) //[1, 3, 2, 5, 4]

Javascript - Put array items, including their duplicates, into a new array

I couldn't find an answer to this specific question on S.O.
Let's say I have an array of strings, or in this case, numbers:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
I'd like the output to be:
var output = [[1,1,1], [2], [3,3,3,3,3], [4], [5, 5, 5]];
I was hoping to use Lodash but most of that stuff tends to remove duplicates rather chunk them together into their own array. Maybe some kind of .map iterator?
The order of the output doesn't really matter so much. It just needs to chunk the duplicates into separate arrays that I'd like to keep.
You can use reduce to group the array elements into an object. Use Object.values to convert the object into an array.
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => {
(c[v] = c[v] || []).push(v);
return c;
}, {}));
console.log(result);
Shorter version:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => ((c[v] = c[v] || []).push(v), c), {}));
console.log(result);
You can do this with Array.reduce in a concise way like this:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = x.reduce((r,c) => (r[c] = [...(r[c] || []), c],r), {})
console.log(Object.values(result))
The exact same with lodash would be:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = _.values(_.groupBy(x))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Using _.values to extract the values of the grouping object and _.groupBy to get the actual groupings
Use Array#prototype#reduce to group them:
const x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let helperObj = {};
const res = x.reduce((acc, curr) => {
// If key is not present then add it
if (!helperObj[curr]) {
helperObj[curr] = curr;
acc.push([curr]);
}
// Else find the index and push it
else {
let index = acc.findIndex(x => x[0] === curr);
if (index !== -1) {
acc[index].push(curr);
}
}
return acc;
}, []);
console.log(res);
Since you're hoping to use Lodash, you might be interested in groupBy. It returns on object, but the _.values will give you the nested array:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let groups = _.values(_.groupBy(x))
console.log(groups)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Here's an imperative solution:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
x.sort();
var res = [];
for (const [i, n] of x.entries()) {
if (n !== x[i-1]) res.push([n]);
else res[res.length-1].push(n);
}
console.log(res);

Organize duplicates into individual array that is ordered

I have an array with numbers. I would like to put the numbers in order and create new array with duplicats in the same array(array in array). Can someone please help me step by step. I would really like to understand
let arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
// I want to create this [[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591]
const sortArray = arr.sort(function(a, b) {
return a - b;
});
You can extract unique values using Set, then sort them (because sorting an array of arrays is more complex), then use array.reduce to acquire all the items in the original array and push either the single value if unique, otherwise the array of values (not sure why you need that, but still..)
Further documentation reference:
What is Set?
Array.reduce
Working code below:
let arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
// I want to create this [[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591]
console.log([...new Set(arr)].sort((a,b) => a - b).reduce((accumulator, next) => {
const filtered = arr.filter(i => i === next);
return accumulator.push(filtered.length === 1 ? filtered[0] : filtered), accumulator
}, []));
You could sort the array and have a look to the last two items and the actual item.
[ 1, 1, 1, 1, 2, 2, 2, 4, 5, 10, 20, 20, 391, 392, 591] array
a b c variables
^ actual item
Then check if the last item b and the actual item c is unequal and return a new array with the the old items and the actual item.
If the item before the last item a and the actual item is unequal, it should be an array for the last item in the result set and the actual item.
Otherwise push the actual item to the nested last array of the result set.
var array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = array
.sort((a, b) => a - b)
.reduce((r, c, i, { [i - 2]: a, [i - 1]: b }) => {
if (b !== c) return [...r, c];
if (a !== c) return r.pop(), [...r, [b, c]];
r[r.length - 1].push(c);
return r;
}, []);
console.log(result);
Although there are other approaches, whenever I need to parse unique values from an array in this way, I will create an object with the object's properties representing the groupings of array values
{ 1: [1, 1, 1], 2: [2 , 2, 2], 4: [4], 5: [5] ...}
Then you can use a native object method to get all keys or values from the object if you need to (or if your end goal is different, you can use the object however it is needed)
Object.keys(obj)
// or
Object.values(obj)
For your case, it would look like
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const obj = arr.reduce((accum, val) => {
accum[val] = accum[val] || [];
accum[val].push(val);
return accum;
}, {});
const finalArr = Object.values(obj).map(val => val.length > 1 ? val : val[0]);
console.log(finalArr);
You can count the occurrences and then use that object to create your final array.
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const count = arr.reduce((acc, val) => {
acc[val] = acc[val] + 1 || 1;
return acc;
}, {});
const result = Object
.keys(count)
.sort((a, b) => a - b)
.map((key) => count[key] === 1 ? +key : Array.from({ length: count[key] }).fill(+key));
console.log(result);
You can do this thing in ways. But if you want to achieve in best way, you must avoid n square loops.
So can create a dictionary of count of values. And loop over the keys of the object in sorted order.
Using Array.reduce to create object of count of array elemnts. and Array.fill to fil an array with same values.
//Given Array
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
//Dictionary with count of each values in array
const arrCountObj = arr.reduce((acc, el) => {
if (acc[el]) {
acc[el] += 1
} else {
acc[el] = 1
}
return acc
}, {})
console.log(arrCountObj)
//Looping over sorted dictionary keys to create array based on condition
var out = Object.keys(arrCountObj).sort((a, b) => a - b).map(x => arrCountObj[x] > 1 ? new Array(arrCountObj[x]).fill(+x) : arrCountObj[x])
console.log(out)
Time Complexity: O(nlogn)
You could:
1) iterate through the array an build a frequency map of the the numbers
2) grab and sort the frequency map key in ascending order
3) build a new array from the information in the frequency map
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const obj = arr.reduce((acc, c) => {
return acc[c] = (acc[c] || 0) + 1, acc;
}, {});
// {"1":4,"2":3,"4":1,"5":1,"10":1,"20":2,"391":1,"392":1,"591":1}
const keys = Object.keys(obj).sort((a, b) => a - b).map(Number);
// [1,2,4,5,10,20,391,392,591]
const out = keys.map((key) => {
return obj[key] > 1 ? Array(obj[key]).fill(key) : key;
});
// [[1,1,1,1],[2,2,2],4,5,10,[20,20],391,392,591]
console.log(JSON.stringify(out));
You can use reduce and then Array.fill. Here the reduce will create an object like this
{
"1": 4,
"2": 3,
"4": 1,
"5": 1,
"10": 1,
"20": 2,
"391": 1,
"392": 1,
"591": 1
}
which mean there are 4 1s, 3 2s and so on. Then you can use array fill after iterating this object. The array fill syntax is arr.fill(value[, start[, end]])
So in our case we will case new Array(k[keys]).fill(+keys, 0, k[keys]) is creating a new array of length 4,3 so on except for 1 and from 0th index it is filling with the key
let arr = [1, 2, 4, 591, 392, 391,1, 2, 5, 10, 2, 1, 1, 1, 20, 20];
let k = arr.reduce(function(acc, curr) {
if (curr in acc) {
acc[curr] += 1;
} else {
acc[curr] = 1
}
return acc;
}, {});
let grouped = [];
for (let keys in k) {
if (k[keys] !== 1) {
grouped.push(new Array(k[keys]).fill(+keys, 0))
} else {
grouped.push(+keys)
}
}
console.log(grouped)

Selectively interleave two arrays

I would like to interleave two arrays, BUT only return pairs when a certain condition is met. As an example:
first_array = [1, 2, 3, 4, 5, 6, 7, 8];
second_array = [, , , , 1, , 0, 1];
I need to return ONLY pairs where array-2 is non-null, in other words, the output I need is:
interleaved = [5, 1, 7, 0, 8, 1];
I have an interleave function that works:
function send_values() {
let interleaved = [];
for (let i = 0; i < first_array.length; i++) {
interleaved.push(first_array[i], second_array[i]);
}
}
...but the output is, obviously:
interleaved = [1, , 2, , 3, , 4, , 5, 1, 6, , 7, 0, 8, 1];
...which is not what I need. Suggestions?
You could iterate the sparse array and take only the values with the values at the same index from array one.
var array1 = [1, 2, 3, 4, 5, 6, 7, 8],
array2 = [, , , , 1, , 0, 1],
result = array2.reduce((r, v, i) => r.concat(array1[i], v), []);
console.log(result);
Here's a generic functional solution:
pairs = (a, b) => a.map((_, i) => [a[i], b[i]])
flat = a => [].concat(...a)
valid = x => x === 0 || Boolean(x)
array_1 = [1, 2, 3, 4, 5, 6, 7, 8];
array_2 = [ , , , , 1, , 0, 1];
result = flat(
pairs(array_1, array_2)
.filter(x => x.every(valid))
)
console.log(result)
Works both ways, that is, it doesn't matter which array contains the null values. Also, you can redefine valid if there are other things to exclude.
As a bonus, if you replace pairs with zip, you can make it work for any number of arrays.

Categories