How to return the element of an array containing the max value (index 1) (Javascript)? - javascript

Given the array below, how can I return the element containing the max number?
let ar = [["finalOrderData",1],["finalFabricData",3],["finalDecorationData",3],["finalHtData",3]]
Expected Result
let ar = ["finalFabricData",3]
This is the function I'm trying with, but it only returns the number itself:
function getMaxOf2DIndex(arr, idx) {
return Math.max.apply(null, arr.map(function (e) { return e[idx] }))
}
Appreciate any help!

Use Array.sort():
let ar = [['finalOrderData', 1], ['finalFabricData', 3], ['finalDecorationData', 3], ['finalHtData', 3]];
let res = ar.sort((a, b) => b[1] - a[1])[0];
console.log(res);
Note that this doesn't handle the alphabetical order of the word.

You can use the Array.reduce function
let ar = [["finalOrderData",1],["finalFabricData",3],["finalDecorationData",3],["finalHtData",3]]
let result = ar.reduce((acc,cur) => !acc || cur[1] > acc[1] ? cur : acc, undefined);
console.log(result);

function getMaxOf2DIndex(arr, idx) {
let maxItem = arr[0];
arr.forEach((item, i) => {
if(item[idx] >= maxItem[idx])
maxItem = item;
});
return maxItem;
}

I think the most straightforward way:
let ar = [["finalOrderData",1],["finalFabricData",3],["finalDecorationData",3],["finalHtData",3]]
var maxNumber = ar[0]
ar.forEach((element) => maxNumber = element[1] > maxNumber[1] ? element : maxNumber)
or the same written more verbosely:
let ar = [["finalOrderData",1],["finalFabricData",3],["finalDecorationData",3],["finalHtData",3]]
var maxNumber = ar[0]
for (element of ar) {
let number = element[1]
if (number > maxNumber[1])
maxNumber = number
}

Here are 2 examples:
const ar = [
["finalOrderData",1],
["finalFabricData",3],
["finalDecorationData",3],
["finalHtData",3]
]
// the 1st result of the biggest value:
const output1 = ar.sort((A,B) => B[1] - A[1])[0]
console.log(output1)
// output: ["finalFabricData",3]
// the last result of the biggest value:
const output2 = ar.sort((A,B) => B[1] <= A[1] ? -1 : 1)[0]
console.log(output2)
// output: ["finalHtData",3]

one more Solution using Array.reduce:
ar.reduce((res, val) => Math.max(res[1],val[1]) === res[1] ? res : val );
trick here is to check with already calculated result.

Related

JS: Sort a part of an array by order

I have a task to sort an array of random numbers by a certain order which is given from another array. All the other elements which could not be sorted should land at the end of the result array:
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
const shouldSortTo = [3,2,1,6,4,5]
I've got the following solution :
array.sort((a,b)=> {
if(sortOrder.indexOf(a) === -1 && sortOrder.indexOf(b) > -1 ) {
return 1
}
if(sortOrder.indexOf(a) > -1 && sortOrder.indexOf(b) === -1 ) {
return -1
}
return sortOrder.indexOf(a) - sortOrder.indexOf(b)
})
It works but I get the feeling that it's not easy to read or understand. Is there a better or shorter way to do it?
Not saying this is better or more efficient but you can first check if all sortOrder items are in the array, then remove sortOrder items from array and concat the two:
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
const shouldSortTo = [3,2,1,6,4,5]
let result = sortOrder.filter(s => array.includes(s)).concat(array.filter(i => !sortOrder.includes(i)))
console.log(result)
Edit:
Based on #epascarello's comment, this may be a better option if array contains duplicate values:
const array = [6,1,2,3,4,5,2,3]
const sortOrder = [3,2,1]
let sorted = sortOrder.reduce((s, i) => [...s, ...array.filter(x => x === i)], [])
let result = sorted.concat(array.filter(i => !sortOrder.includes(i)))
console.log(result)
I would not keep lookin up the index. Just read it once and use it. You can apply it to your own code.
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
array.sort((a,b)=> {
const aI = sortOrder.indexOf(a);
const bI = sortOrder.indexOf(b);
if (aI === -1 && bI > -1 ) return 1;
else if (aI > -1 && bI === -1 ) return -1;
return aI - bI;
})
console.log(array);
I would check if they are both equal and return zero.
I would check for either to be -1.
And finally I would sort base on the index.
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
array.sort((a,b)=> {
const aI = sortOrder.indexOf(a);
const bI = sortOrder.indexOf(b);
if (aI === bI) return 0;
else if (aI === -1) return 1;
else if (bI === -1) return -1;
else return aI - bI;
})
console.log(array);
Other option, if -1, set to the array's length and just subtract
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
const max = array.length;
array.sort((a,b)=> {
const aI = sortOrder.indexOf(a);
const bI = sortOrder.indexOf(b);
return (aI === -1 ? max : aI) - (bI === -1 ? max : bI);
})
console.log(array);
Or you can reserve the logic
const array = [6,1,2,3,4,5]
const sortOrder = [3,2,1]
const max = array.length;
const reversed = sortOrder.slice().reverse();
array.sort((a,b)=> {
const aI = max - reversed.indexOf(a);
const bI = max - reversed.indexOf(b);
return aI - bI;
})
console.log(array);

Inconsistency, when returning index of duplicate values

I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);

Javascript - Counting array elements by reduce method until specific value occurs doesn't give a correct output

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total)=>(num==0 ? total : total+num), 0)
console.log(sum(arr, 0))
Please check how can I make it work. Did some mistake but don't know what exactly. Output is a function instead of a result.
This is awkward to do in .reduce because it goes through the entire array. If we do a naive implementation you can see the problem:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0)
console.log(sum(arr, 0))
We now make the check correctly - num==x will return true when x is zero (the value of num). However, the result is wrong because this only returns true once but any other iteration it's still true. And here is the same thing with more logging that describes each step of the process:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
const boolCheck = num==x;
const result = boolCheck ? total : total+x;
console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);
return result;
}, 0)
console.log(sum(arr, 0))
So, you need to add some flag that persists between iterations, so it doesn't get lost.
One option is to have an external flag that you change within the reduce callback:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
let finished = false;
return arr.reduce((total, x) => {
if(x === num)
finished = true;
return finished ? total : total+x;
}, 0)
}
console.log(sum(arr, 0))
Alternatively, you can have that flag internal to the reduce callback and pass it around between calls. It works the same way in the end but makes the callback function pure. At the cost of some unorthodox construct:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
return arr.reduce(({total, finished}, x) => {
if(x === num)
finished = true;
total = finished ? total : total+x;
return {total, finished};
}, {total: 0, finished: false})
.total
}
console.log(sum(arr, 0))
If you want to use reduce but you're OK with using other methods, then you can use Array#indexOf to find the first instance of a value and Array#slice the array that contains any value up to the target value:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
const endIndex = arr.indexOf(num);
return arr.slice(0, endIndex)
.reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))
Or in as one chained expression:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
.slice(0, arr.indexOf(num))
.reduce((total, x)=> total+x, 0);
console.log(sum(arr, 0))
Other libraries may have a takeUntil or takeWhile operation which is even closer to what you want - it gets you an array from the beginning up to a given value or condition. You can then reduce the result of that.
Here is an example of this using Lodash#takeWhile
By using chaining here, Lodash will do lazy evaluation, so it will only go through the array once, instead of scanning once to find the end index and going through the array again to sum it.
const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
.takeWhile(x => x !== num)
.reduce((total, x)=>total+x, 0)
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
As a note, if you are using Lodash, then you may as well use _.sum(). I didn't above just to illustrate how a generic takeUntil/takeWhile looks.
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
.takeWhile(x => x !== num)
.sum()
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Since you need to stop summing values part way through the array, this might be most simply implemented using a for loop:
const arr = [5, 6, 0, 7, 8];
const num = 0;
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == num) break;
sum += arr[i];
}
console.log(sum);
If you want to use reduce, you need to keep a flag that says whether you have seen the num value so you can stop adding values from the array:
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => {
let seen = false;
return arr.reduce((c, v) => {
if (seen || v == num) {
seen = true;
return c;
}
return c + v;
}, 0);
}
console.log(sum(arr, 0));
console.log(sum(arr, 8));
call it as follows:
console.log(sum(arr, 0)());
You need parenthesis to execute the function ()
sum(arr, 0)
Without parenthesis you store a reference to the function in the variable

Find the first unique value in an array or string

How can I get one unique value in an array or string? Only the first value. Pure JS only.
My example:
function searchValue () {
let inputText = [1,1,4,2,2,2,3,1];
let foundedValue;
for (let i = 0; i < inputText.length; i++) {
if (i === inputText.indexOf(inputText[i]) && i === inputText.lastIndexOf(inputText[i])) {
foundedValue = inputText[i];
break;
} else {
foundedValue = "not founded.";
}
}
return foundedValue;
}
console.log("Search value: "+ searchValue())
Answer is 4.
But, I need a short solution. Using the find() and filter() functions.
You can find the first unique item in your array using find() and comparing indexOf() to lastIndexOf() to determine whether or not there is more than one instance of an item in the array. If you need to find unique characters in a string, then you can first split it into an array and then use the same approach.
const arr = [1, 1, 4, 2, 2, 2, 3, 1];
const result = arr.find((x) => arr.indexOf(x) === arr.lastIndexOf(x));
console.log(result);
// 4
const text = 'aadbbbca';
const textarr = text.split('');
const textresult = textarr.find((x) => textarr.indexOf(x) === textarr.lastIndexOf(x));
console.log(textresult);
// d
You can try this.
const arr = [1, 1, 4, 2, 2, 2, 3, 1];
let r = {};
arr.map(a => r[a] = (r[a] || 0) +1)
var res = arr.find(a => r[a] === 1 )
console.log(res)
You can use js Set() object.
At first you could create a Set of duplicated elements.
const inputText = [1,1,4,2,2,2,3,1];
const duplicatesSet= inputText.reduce((dupSet, el) =>
inputText.filter(arrEl => arrEl === el).length > 1 ?
dupSet.add(el) : dupSet
, new Set());
Second you could use array.find. It returns first duplicated element.
const firstDupElement = inputText.find(el => duplicatesSet.has(el));
const searchValue = (_param) => {
for (let i= 0; i < _param.length; i+= 1) {
if (_param.indexOf(_param[i]) === _param.lastIndexOf(_param[i])) return _param[i];
}
return "not founded.";
}
let arr = [1,1,2,2,2,1,3,1,4,4,5]
const dupelearray = (array) => {
let arr2 =[...arr]
let ele = []
let state = false
arr2.map((i,index)=>{
arr2.splice(index,1)
arr.map((j)=>{
return arr2.includes(j) ? null : state=true
})
state && ele.push(i)
state=false
arr2.splice(index,0,i)
})
return console.log(arr.indexOf(ele[0]))
}
dupelearray(arr)
wow i didnt knew lastindexof method and was making this algo so difficult
btw this solution also works but definitely i am new in algo so this will take much more time but the solution still works!!!!!! damn should remember more methods or you have to think so much -_-

Serializing Array of Many Duplicates

So I have a series of arrays, each of which are 2500 long, and I need to serialize and store all them in very limited space.
Since I have many duplicates, I wanted to cut them down to something like below.
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
// to
[0x4,2,7,3x2,0x9]
I wrote a couple one-liners (utilising Lodash' _.repeat) to convert to and from this pattern, however converting to doesn't seem to work in most/all cases.
let serialized = array.toString().replace(/((?:(\d)+,?)((?:\2+,?){2,}))/g, (m, p1, p2) => p2 + 'x' + m.replace(/,/g, '').length);
let parsed = serialized.replace(/(\d+)x(\d+),?/g, (z, p1, p2) => _.repeat(p1 + ',', +p2)).split(',');
I don't know why it doesn't work. It may be due to some of the numbers in the array. Eye-balling, the largest one is 4294967295, however well over 90% is just 0.
What am I missing in my RegEx that's preventing it from working correctly? Is there a simpler way that I'm too blind to see?
I'm fairly confident with converting it back from the serialized state, just need a hand getting it to the state.
Straight forward and simple serialization:
let serialize = arr => {
const elements = [];
const counts = []
let last = undefined;
[0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0].forEach((el,i,arr)=>{
if (el!==last) {
elements.push(el);
counts.push(1);
} else {
counts[counts.length-1]++;
}
last = el;
})
return elements.map((a,i)=>counts[i]>1?`${a}x${counts[i]}`:a).join(",");
};
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
UPDATE
Pure functional serialize one:
let serialize = arr => arr
.reduce((memo, element, i) => {
if (element !== arr[i - 1]) {
memo.push({count: 1, element});
} else {
memo[memo.length - 1].count++;
}
return memo;
},[])
.map(({count, element}) => count > 1 ? `${count}x${element}` : element)
.join(",");
console.log(serialize([0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]));
Pure functional deserialize:
const deserialize = str => str
.split(",")
.map(c => c.split("x").reverse())
.reduce((memo, [el, count = 1]) => memo.concat(Array(+count).fill(+el)), []);
console.log(deserialize("4x0,2,7,2x3,9x0"))
In order to avoid using .reverse() in this logic, I'd recommend to change serialization from 4x0 to 0x4
Try this
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0];
var finalArray = []; //array into which count of values will go
var currentValue = ""; //current value for comparison
var tmpArr = []; //temporary array to hold values
arr.forEach( function( val, index ){
if ( val != currentValue && currentValue !== "" )
{
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
tmpArr = [];
}
tmpArr.push(val);
currentValue = val;
});
finalArray.push( tmpArr.length + "x" + tmpArr[0] );
console.log(finalArray);
Another version without temporary array
var arr = [0, 0, 0, 0, 2, 7, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var finalArray = []; //array into which count of values will go
var tmpCount = 0; //temporary variable to hold count
arr.forEach(function(val, index) {
if ( (val != arr[ index - 1 ] && index !== 0 ) )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
tmpCount = 0;
}
tmpCount++;
if ( index == arr.length - 1 )
{
finalArray.push(tmpCount + "x" + arr[ index - 1 ] );
}
});
console.log(finalArray);
Do not use RegEx. Just use regular logic. I recommend array.reduce for this job.
const arr1 = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0]
const arr2 = ['0x4','2','7','3x2','0x9'];
const compact = arr => {
const info = arr.reduce((c, v) =>{
if(c.prevValue !== v){
c.order.push(v);
c.count[v] = 1;
c.prevCount = 1;
c.prevValue = v;
} else {
c.prevCount = c.prevCount + 1;
c.count[v] = c.count[v] + 1;
};
return c;
},{
prevValue: null,
prevCount: 0,
count: {},
order: []
});
return info.order.map(v => info.count[v] > 1 ? `${v}x${info.count[v]}` : `${v}`);
}
const expand = arr => {
return arr.reduce((c, v) => {
const split = v.split('x');
const value = +split[0];
const count = +split[1] || 1;
Array.prototype.push.apply(c, Array(count).fill(value));
return c;
}, []);
}
console.log(compact(arr1));
console.log(expand(arr2));
This is a typical reducing job. Here is your compress function done in just O(n) time..
var arr = [0,0,0,0,2,7,3,3,0,0,0,0,0,0,0,0,0],
compress = a => a.reduce((r,e,i,a) => e === a[i-1] ? (r[r.length-1][1]++,r) : (r.push([e,1]) ,r),[]);
console.log(JSON.stringify(compress(arr)));
since the motivation here is to reduce the size of the stored arrays, consider using something like gzip-js to compress your data.

Categories