Create a sorted array by mistake using .map() - javascript

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]

Related

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

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

Relative Sort Array Javascript

I am working on a problem on LeetCode and having some troubles
https://leetcode.com/problems/relative-sort-array/
Instructions:
Given two arrays arr1 and arr2, the elements of arr2 are distinct, and all elements in arr2 are also in arr1.
Sort the elements of arr1 such that the relative ordering of items in arr1 are the same as in arr2. Elements that don't appear in arr2 should be placed at the end of arr1 in ascending order.
Example 1:
Input: arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
Output: [2,2,2,1,4,3,3,9,6,7,19]
my attempt:
var relativeSortArray = function(arr1, arr2) {
let arr =[]
let end =[]
for (i=0; i<arr2.length; i++){
for (j=0; j<arr1.length; j++){
if(arr2[i] == arr1[j]){
arr.push(arr1[j])
}else{
end.push(arr1[j])
}
}
}
end.sort((a,b) => a-b)
console.log(end)
return arr
};
The If conditional works but the else condition isn't and I can't figure out why.
I think console.log(end) should give me the two numbers not in arr2 but it instead gives me:
[
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 6,
6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 9, 9,
9, 9, 9, 19, 19, 19, 19, 19, 19
]
Why is this happening?
Thanks!!!
You could take an object for the position of a value and take a large value like Number.MAX_VALUE as default value. If the delta is zero sort by value.
Taking a delta is a standard by using Array#sort. This returns a value smaller than zero, zero or greater than zero, depending on the values. The sort method receives this values and keeps or swaps the values.
const
relativeSort = (array, given) => {
const order = Object.fromEntries(given.map((v, i) => [v, i + 1]));
return array.sort((a, b) =>
(order[a] || Number.MAX_VALUE) - (order[b] || Number.MAX_VALUE) ||
a - b
);
};
console.log(...relativeSort([2, 3, 1, 3, 2, 4, 6, 7, 9, 2, 19], [2, 1, 4, 3, 9, 6]));
In each iteration of arr2:
all the numbers that are different from the current number are pushed to the end array
For example,
First iteration - compare number (2) and you will end up with:
arr: [2,2,2]
end: [3,1,3,4,6,7,9,19]
Second iteration - compare number (1) and you will end up with:
arr: [2,2,2,1]
end: [3,1,3,4,6,7,9,19] + [2,3,3,2,4,6,7,9,2,19]
try to debug your code to follow the flow
class Solution:
def relativeSortArray(self, arr1: list[int], arr2: list[int]) -> list[int]:
arr = []
for i in arr2:
value = arr1.count(i)
for j in range(value):
arr.append(i)
arr1.remove(i)
arr1.sort()
return arr+arr1
obj = Solution()
arr1 = [28,6,22,8,44,17]
arr2 = [22,28,8,6]
result = obj.relativeSortArray(arr1,arr2)
print(result)

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

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.

Get item what's not on the 2nd, 3rd arrays - JS

I am trying to create a function that will get the items that cannot be seen on the 2nd or 3rd and upcoming arrays passed within the function.
Right now my function gets only the similar items. How can I make it get the difference (w/c are the items that doesn't exist to the 2nd and 3rd and proceeding arrays.
const callM = function(arrays) {
arrays = Array.prototype.slice.call(arguments);
let result = [];
for(let i = 1; i < arrays.length; i++){
for(let x = 0; x < arrays[i].length; x++){
if(arrays[0].includes(arrays[i][x])){
result.push(arrays[i][x]);
}
}
}
return result;
};
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The logic right now is a bit off as it gets the opposite. How do i fix this?
Also is there a way to do this using Higher Order functions such as reduce or filter?
Thanks!
I'd think about this differently. As the difference between two sets: array 0 and array 1...n
To get array 0, just shift it off the top
const arr0 = arrays.shift()
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
This removes the first array from arrays
Next we combine the remaining arrays
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
Ref: http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/
Unneeded, handled by includes as mentioned by #Phil
Next filter duplicates from arrN by comparing with itself
const unique = arrN.filter(function(elem, index, self) {
return index == self.indexOf(elem);
})
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Next filter with includes to find the difference (or union)
const diff = arr0.filter(function(item) {
return !arrN.includes(item))
}
Full snippet:
function callM(arrays) {
const arr0 = arrays.shift()
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
//const unique = arrN.filter(function(elem, index, self) {
// return index == self.indexOf(elem)
//})
return arr0.filter(function(item) {
return !arrN.includes(item)
})
}
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10]]))
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]]))
of course ES6 would be easier. ;)
const callM = (first, ...rest) => {
const arrays = [].concat(...rest)
return first.filter(item => !arrays.includes(item))
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
A short solution for small and medium sized arrays:
// Return elements in array but not in filters:
function difference(array, ...filters) {
return array.filter(el => !filters.some(filter => filter.includes(el)));
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 1, 10], [7, 2, 8])); // [3, 4]
For large inputs, consider creating a Set from all filters and filtering in linear time using set.has(el).
In order to fix your implementation, you could label the outer for-loop and continue from there whenever a filter contains one of the array elements. Only when all filters pass without match, you push the array element into the result:
// Return elements in array but not in filters:
function difference(array, ...filters) {
const result = [];
loop: for (const el of array) {
for (const filter of filters) {
if (filter.includes(el)) continue loop;
}
result.push(el);
}
return result;
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3,4]
If you're willing to use Underscore, you can do this in one line of code:
console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
https://jsfiddle.net/o1zuaa6m/
You can use array#reduce to create object lookup of all the other array excluding the first array. Then use array#filter to get the values which are not present in the object lookup
var callM = (first, ...rest) => {
var combined = rest
.reduce((res,arr) => res.concat(arr))
.reduce((o, v) => {
o[v] = true;
return o;
},{});
return first
.filter(v => !combined[v]);
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The "proper" way to exclude values is usually to use a lookup hash set with the values to exclude:
const callM = (a, ...b) => (b = new Set(b.concat.apply(...b)), a.filter(v => !b.has(v)))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3, 4]

Categories