What is the javascript equivalent of numpy argsort? - javascript

I want to sort the imgUrl array by click count. I have two arrays.
clickCount = [5,2,4,3,1]
imgUrl = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg']
In numpy it is easy. I use order = np.argsort(clickCount) then I create another array newArray = [imgUrl[i] for i in order].
How do I achieve the same effect in javascript (preferably vanilla)?

You can use a Schwartzian transform also known as Decorate-Sort-Undecorate (DSU) in python.
DSU:
Decorate - Use Array#Map to enrich each item in the array with the needed sort data
Sort - sort using the added data
Undecorate - extract the sorted data using Array#map again
Demo:
const dsu = (arr1, arr2) => arr1
.map((item, index) => [arr2[index], item]) // add the args to sort by
.sort(([arg1], [arg2]) => arg2 - arg1) // sort by the args
.map(([, item]) => item); // extract the sorted items
const clickCount = [5,2,4,3,1];
const imgUrl = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg'];
const result = dsu(imgUrl, clickCount);
console.log(result);
thanks to dankal444 for the refactor to the function

For completeness, here's my solution to the actual answer (providing argsort function), by expanding on Ori's answer with DSU.
Since sort is by default taking the first element, so implementing it as DSU is merely adding an index, sorting it, then taking the indices.
let decor = (v, i) => [v, i]; // set index to value
let undecor = a => a[1]; // leave only index
let argsort = arr => arr.map(decor).sort().map(undecor);
clickCount = [5, 2, 4, 3, 1]
imgUrl = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg']
order = argsort(clickCount);
newArray = order.map(i => imgUrl[i])
console.log(newArray);

Functional approach (like #Ori Drori's code) is always a charm to watch, but in this case, you only need to re-arrange an array's items. I believe that there is a simpler way to go and is a much easier code to read.
const clickCount = [5,2,4,3,1];
const imgUrl = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg'];
sortByArrayRefOrder = (data, orderRefArr) => {
let orderedArr = [], i=0;
orderRefArr.map( o => { orderedArr[o-1] = data[i++]});
return orderedArr.reverse();
}
console.log ( sortByArrayRefOrder(imgUrl, clickCount) );

Related

Do anyone understand this reduce method problem Im having?

Do anyone understand why console.log(posArr) is printing [1,2,3] and not [0,1,2]. Im trying to push to nOfPos all the index positions of my array :)
function combinations(x) {
let arr = x.toString().split('');
console.log(arr)
let nOfPos = [];
let posArr = arr.map(x => nOfPos.push(arr.indexOf(arr[x])));
let mult = posArr.reduce((acum, item) => acum * item);
console.log(posArr);
console.log(mult);
}
combinations(123)
The problem is due to two errors in your code. Firstly push() returns the new length of the array, so that's why the mapped output contains the incremental 1,2,3....
Secondly, you need to search for x within arr, so you only need to pass x to indexOf().
With those issues corrected the code works:
function combinations(x) {
let arr = x.toString().split('');
let posArr = arr.map(x => arr.indexOf(x));
let mult = posArr.reduce((acum, item) => acum * item);
console.log(posArr);
console.log(mult);
}
combinations(123)
That being said, the purpose of posArr is a little redundant; it will always be an array of incremental values, up to the length of the string -1.
In addition, mult is redundant too as it will always be 0, as you are multiplying by 0.
You could use a 'for in' loop to get the array indexes.
let posArr = [];
for (ix in arr) {
posArr.push(ix)
}

Merging two Array one after another Javascript [equal length]

I need to merge two array into one,one after another is there any better way?
for example ,
const arr1 = [1,2,3,4,5];
const arr2 = [a,b,c,d,e];
const resultIWant = [1,a,2,b,3,c,4,d,5,e]
Short and clear if both arrays are same length:
const arr1 = [1,2,3,4,5]
const arr2 = ['a','b','c','d','e']
const res = arr1.flatMap((e, idx) => [e, arr2[idx]])
console.log(res)
This can be done fairly easily using the reduce method. Here is my example below, I iterate through arr1 using reduce() and as I push the current value (c) to the accumulator (a), I also push the value from arr2 of the same index.
In my code below, this looks like a.push(c, arr2[i]) but you could also achieve this using a.push(arr1[i], arr2[i]), using arr1[i] instead of c if you want to keep both push values looking consistent.
const arr1 = [1,2,3,4,5];
const arr2 = ['a','b','c','d','e'];
const result = arr1.reduce((a,c,i) => (a.push(c, arr2[i]), a), []);
console.log(result);

How to compare a part of an array item with a part of an array item from a second array?

If I have two arrays with files
arr1 = ['file1.webp', 'file2.webp', ...];
arr2 = ['file1.jpg', 'file2.png', 'file3.jpg', 'file4.jpg', ...];
how would I check which array items are equal, minus the *.format part?
The idea is that, if there are two equal items, a webp and an alternative source are available. While if an item has no match, no webp source was provided. Both cases would lead to different image handling later on.
I could compare the items inside two arrays like so: let match = arr1.find( val => arr2.includes(val) );
But this compares each entire item. I only want to compare the file names. The formats in which the files were provided need to be cut off, so I get:
arr1 = ['file1', 'file2', ...];
arr2 = ['file1', 'file2', 'file3', 'file4', ...];
I can then filter out all matches between the two arrays. I've been searching for a solution for a real while, but I'm still not sure how to get there.
With a function that trims off the file extension, you can construct a Set of one of the transformed arrays. Then iterate over the other array and check whether its transformed item is in the Set or not:
const arr1 = ['file1.webp', 'file2.webp'];
const arr2 = ['file1.jpg', 'file2.png', 'file3.jpg', 'file4.jpg'];
const transform = str => str.replace(/\.[^.]+$/, '');
const set1 = new Set(arr1.map(transform));
for (const item of arr2) {
if (set1.has(transform(item))) {
console.log('Match for', item);
} else {
console.log('No match for', item);
}
}
You can use filter() with nested some(). To get the file name from complete name use split('.')and get the first element using .split('.')[0]
let arr1 = ['file1.webp', 'file2.webp'];
let arr2 = ['file1.jpg', 'file2.png', 'file3.jpg', 'file4.jpg'];
let res = arr2.filter(a => arr1.some(b => a.split('.')[0] === b.split('.')[0]));
console.log(res)
You could filter by looking to the right side.
const getName = s => s.replace(/\.[^.]+$/, '');
var array1 = ['file1.webp', 'file2.webp'],
array2 = ['file1.jpg', 'file2.png', 'file3.jpg', 'file4.jpg'],
set1 = new Set(array1.map(getName)),
common = array2.filter(s => set1.has(getName(s)));
console.log(common);
write extract method to get value to compare. Just use the extract method in your code. Alternatively, you can build an arr2Obj to not to repeat the searches.
const arr1 = ["file1.webp", "file2.webp"];
const arr2 = ["file1.jpg", "file2.png", "file3.jpg", "file4.jpg"];
const extract = item => item.split(".")[0];
let match = arr1.find(val => arr2.map(x => extract(x)).includes(extract(val)));
console.log(match);
// Alternatively,
const arr2Obj = Object.assign({}, ...arr2.map(x => ({ [extract(x)]: 1 })));
const match2 = arr1.find(val => extract(val) in arr2Obj);
console.log(match2);

Map and filter an array JavaScript

I’m trying to map a nested array and return the words that have more letters than 6, I have been stuck for a while with this problem so I’d like to get some help
const array = [["hellow",'pastas'],["travel", "militarie"],["oranges","mint"]]
const arrayOne = array.map(new => new).filter(arr =>arr.length > 6)
You can flat array first and than filter out words with length greater than 6
const array = [['hellow','pastas'],['travel', 'militaries'],['oranges','mint']]
const arrayOne = array.flat(1).filter(e=> e.length > 6 )
console.log(arrayOne)
You can use the code below. This code uses .map() and .filter() to check if the length is greater than 6, and adds it to the array if it is.
const array = [["hellow","pastas"],["travel", "militarie"],["oranges","mint"]];
const arrayOne = array.map(e1 => e1.filter(e2 => e2.length > 6)).flat();
console.log(arrayOne);
I think better will be the filter() method instead.
array.filter(function (c) {
return c.length < 6;
});
But first use the flat() method.
There are many ways to do it.
You can use flatMap and filter:
const array = [['hellow','pastas'],['travel', 'militarie'],['oranges','mint']];
const result = array.flatMap(x => x.filter(y => y.length > 6));
console.log(result);
Another way is to use reduce and filter:
const array = [['hellow','pastas'],['travel', 'militarie'],['oranges','mint']];
const result = array.reduce((acc, x) => [...acc, ...x.filter(y => y.length > 6)], []);
console.log(result);

Array 'map' vs 'forEach' - functional programming

I have an array of objects:
let reports = [{ inbound_calls: [...], outbound_calls: [...], outbound_national_calls: [...] },...];
What is the best way to create a new array and assign into a variable:
1st approach - one loop:
let inbound_calls = []; outbound_national_calls = [], outbound_calls = [];
reports.forEach((e) => {
inbound_calls.push(e.inbound_calls);
outbound_national_calls.push(e.outbound_national_calls);
outbound_calls.push(e.outbound_calls);
})
2nd approach:
let inbound_calls = this.reports.map((report) => report.inbound_calls)
let outbound_national_calls = this.reports.map((report) => report.outbound_national_calls)
let outbound_calls = this.reports.map((report) => report.outbound_calls)
I'm starting to learn functional programming, and want to apply it to my code, I would go with first approach (one loop), but as I did research about functional programming I think the second one is the right way (much cleaner) but, I'm not sure, what is less expensive operation?
If your ultimate goal is to create three variables out of the object, you may use object destructuring as follows. No loops required.
let reports = {
inbound_calls: [1, 2, 3],
outbound_calls: [4, 5, 6],
outbound_national_calls: [7, 8, 9]
};
let {inbound_calls, outbound_calls, outbound_national_calls} = reports;
console.log(inbound_calls);
console.log(outbound_calls);
console.log(outbound_national_calls);
If you want to copy the arrays, just use Array#slice (the 0 passed is optional as it is the default start index so you can omit it if you want) like:
let inbound_calls = reports.inbound_calls.slice(0),
outbound_national_calls = reports.outbound_national_calls.slice(0),
outbound_calls = reports.outbound_calls.slice(0);
or Array.from like:
let inbound_calls = Array.from(reports.inbound_calls),
outbound_national_calls = Array.from(reports.outbound_national_calls),
outbound_calls = Array.from(reports.outbound_calls);
What you're essentially doing is a matrix transposition:
const report = (inbound_calls, outbound_calls, outbound_national_calls) =>
({ inbound_calls, outbound_calls, outbound_national_calls });
const reports = [report(1,2,3), report(4,5,6), report(7,8,9)];
const transpose = reports =>
report( reports.map(report => report.inbound_calls)
, reports.map(report => report.outbound_calls)
, reports.map(report => report.outbound_national_calls) );
console.log(transpose(reports));
Now, depending upon your application the fastest way to transpose a matrix might be not to transpose it at all. For example, suppose you have a matrix A and its transpose B. Then, it holds that for all indices i and j, A[i][j] = B[j][i]. Consider:
const report = (inbound_calls, outbound_calls, outbound_national_calls) =>
({ inbound_calls, outbound_calls, outbound_national_calls });
const reports = [report(1,2,3), report(4,5,6), report(7,8,9)];
// This is equivalent to transpose(reports).outbound_calls[1]
const result = reports[1].outbound_calls;
console.log(result);
That being said, your second approach is IMHO the most readable.

Categories

Resources