Incrementing a value in an object - javascript

I'm working on finding the mode for a given array. The way I've gone about it so far is to reduce the array into object keys, and set each of the key values to zero. Now I want to scan the array again and increment the value by 1 for each occurrence of the element in the array.
I have what I think should work, but the key with multiple occurrences isn't getting incremented. All values just read as 1.
My code so far:
const mode = function(arr) {
const reducer = arr.reduce((accumulator, currentValue) => (accumulator[currentValue] = 0, accumulator), {});
for (let key in reducer) {
for (let i = 0; i < arr.length; i++) {
if (reducer[key] === arr[i]) {
reducer[key]++;
}
}
}
return reducer;
};
console.log(mode([6,2,3,4,9,6,1,0,5]));
Which returns:
{ '0': 1, '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '9': 1 }
What am I missing to change '6': 1 to '6': 2 ?
Any guidance is appreciated. Still getting the hang of things, open to any suggestions or feedback. Thank you!

The error in your code (as noted by #Pointy) is the comparison reducer[key] === arr[i] that should be if (key === arr[i]) {. Since you are using strict equality (===) you'll need to cast the key to a number, or use the equality (==) operator instead.
const mode = function(arr) {
const reducer = arr.reduce((accumulator, currentValue) => (accumulator[currentValue] = 0, accumulator), {});
for (let key in reducer) {
for (let i = 0; i < arr.length; i++) {
if (+key === arr[i]) {
reducer[key]++;
}
}
}
return reducer;
};
console.log(mode([6, 2, 3, 4, 9, 6, 1, 0, 5]));
However, you are doing some redundant work, since you can compute the number of occurrences when you reduce the array to an object:
const mode = function(arr) {
return arr.reduce((accumulator, currentValue) => {
accumulator[currentValue] = (accumulator[currentValue] ?? 0) + 1;
return accumulator;
} , {});
};
console.log(mode([6,2,3,4,9,6,1,0,5]));

Related

Sum of similar value in n X n dimensional array with n^2 complexity

Given an array [[1, 7, 3, 8],[3, 2, 9, 4],[4, 3, 2, 1]],
how can I find the sum of its repeating elements? (In this case, the sum would be 10.)
Repeated values are - 1 two times, 3 three times, 2 two times, and 4 two times
So, 1 + 3 + 2 + 4 = 10
Need to solve this problem in the minimum time
There are multiple ways to solve this but time complexity is a major issue.
I try this with the recursion function
How can I optimize more
`
var uniqueArray = []
var sumArray = []
var sum = 0
function sumOfUniqueValue (num){
for(let i in num){
if(Array.isArray(num[i])){
sumOfUniqueValue(num[i])
}
else{
// if the first time any value will be there then push in a unique array
if(!uniqueArray.includes(num[i])){
uniqueArray.push(num[i])
}
// if the value repeats then check else condition
else{
// we will check that it is already added in sum or not
// so for record we will push the added value in sumArray so that it will added in sum only single time in case of the value repeat more then 2 times
if(!sumArray.includes(num[i])){
sumArray.push(num[i])
sum+=Number(num[i])
}
}
}
}
}
sumOfUniqueValue([[1, 7, 3, 8],[1, 2, 9, 4],[4, 3, 2, 7]])
console.log("Sum =",sum)
`
That's a real problem, I am just curious to solve this problem so that I can implement it in my project.
If you guys please mention the time it will take to complete in ms or ns then that would be really helpful, also how the solution will perform on big data set.
Thanks
I would probably use a hash table instead of an array search with .includes(x) instead...
And it's also possible to use a classical for loop instead of recursive to reduce call stack.
function sumOfUniqueValue2 (matrix) {
const matrixes = [matrix]
let sum = 0
let hashTable = {}
for (let i = 0; i < matrixes.length; i++) {
let matrix = matrixes[i]
for (let j = 0; j < matrix.length; j++) {
let x = matrix[j]
if (Array.isArray(x)) {
matrixes.push(x)
} else {
if (hashTable[x]) continue;
if (hashTable[x] === undefined) {
hashTable[x] = false;
continue;
}
hashTable[x] = true;
sum += x;
}
}
}
return sum
}
const sum = sumOfUniqueValue2([[1, 7, 3, 8],[[[[[3, 2, 9, 4]]]]],[[4, 3, 2, 1]]]) // 10
console.log("Sum =", sum)
This is probably the fastest way...
But if i could choose a more cleaner solution that is easier to understand then i would have used flat + sort first, chances are that the built in javascript engine can optimize this routes instead of running in the javascript main thread.
function sumOfUniqueValue (matrix) {
const numbers = matrix.flat(Infinity).sort()
const len = numbers.length
let sum = 0
for (let i = 1; i < len; i++) {
if (numbers[i] === numbers[i - 1]) {
sum += numbers[i]
for (i++; i < len && numbers[i] === numbers[i - 1]; i++);
}
}
return sum
}
const sum = sumOfUniqueValue2([[1, 7, 3, 8],[[[[[3, 2, 9, 4]]]]],[[4, 3, 2, 1]]]) // 10
console.log("Sum =", sum)
You could use an objkect for keeping trak of seen values, like
seen[value] = undefined // value is not seen before
seen[value] = false // value is not counted/seen once
seen[value] = true // value is counted/seen more than once
For getting a value, you could take two nested loops and visit every value.
Finally return sum.
const
sumOfUniqueValue = (values, seen = {}) => {
let sum = 0;
for (const value of values) {
if (Array.isArray(value)) {
sum += sumOfUniqueValue(value, seen);
continue;
}
if (seen[value]) continue;
if (seen[value] === undefined) {
seen[value] = false;
continue;
}
seen[value] = true;
sum += value;
}
return sum;
},
sum = sumOfUniqueValue([[1, 7, 3, 8], [3, 2, 9, 4], [4, 3, 2, 1]]);
console.log(sum);
Alternatively take a filter and sum the values. (it could be more performat with omitting same calls.)
const
data = [[1, 7, 3, 8], [3, 2, 9, 4, 2], [4, 3, 2, 1]],
sum = data
.flat(Infinity)
.filter((v, i, a) => a.indexOf(v) !== a.lastIndexOf(v) && i === a.indexOf(v))
.reduce((a, b) => a + b, 0);
console.log(sum);
You can flatten the array, filter-out single-instance values, and sum the result:
const data = [
[ 1, 7, 3, 8 ],
[ 3, 2, 9, 4 ],
[ 4, 3, 2, 1 ]
];
const numbers = new Set( data.flat(Infinity).filter(
(value, index, arr) => arr.lastIndexOf(value) != index)
);
const sum = [ ...numbers ].reduce((a, b) => a + b, 0);
Another approach could be the check the first and last index of the number in a flattened array, deciding whether or not it ought to be added to the overall sum:
let sum = 0;
const numbers = data.flat(Infinity);
for ( let i = 0; i < numbers.length; i++ ) {
const first = numbers.indexOf( numbers[ i ] );
const last = numbers.lastIndexOf( numbers[ i ] );
if ( i == first && i != last ) {
sum = sum + numbers[ i ];
}
}
// Sum of numbers in set
console.log( sum );

Count function and number

Write a function - countNumbers It should accept string with different symbols and return an object which contains counts of each number.
Tip: consider reusing makeNumber function.
My solution is not full, what should i do the next?
function countNumbers (string) {
let numbers = [];
for (let i = 0; i < string.length; i++) {
if (!isNaN(parseInt(string[i]))){
numbers.push([string[i]])
}
}
return numbers.join('');
};
for example: countNumbers('erer384jj4444666888jfd123');
// => {'1': 1, '2': 1, '3': 2, '4': 5, '6': 3, '8': 4}
countNumbers('jdjjka000466588kkkfs662555');
// => {'0': 3, '2': 1, '4': 1, '5': 4, '6': 4, '8': 2}
You can use an object instead of an array, and check if the property already exists. If it does then add 1, else set it to start with 1.
In your code you return a string with return numbers.join(''); but in this case your can return the object instead.
function countNumbers(string) {
let numbers = {};
for (let i = 0; i < string.length; i++) {
let val = string[i];
if (!isNaN(parseInt(val))) {
val in numbers ? numbers[val]++ : numbers[val] = 1;
}
}
return numbers;
}
console.log(countNumbers('erer384jj4444666888jfd123'));
console.log(countNumbers('jdjjka000466588kkkfs662555'));
Or as pointed out by #secan as an alternative solution you can use a pattern [0-9] with the global flag /g to get the digits from the string.
const countNumbers = str => {
const counterObj = {};
str.match(/[0-9]/g).forEach(
s => counterObj.hasOwnProperty(s) ? counterObj[s] += 1 : counterObj[s] = 1
);
return counterObj;
}
console.log(countNumbers('erer384jj4444666888jfd123'));
console.log(countNumbers('jdjjka000466588kkkfs662555'));

Grouping items with more that one indexes into sub arrays

Trying to create a function that groups repeated items in an array into sub arrays, and also grouping strings (should there be any) into another subarray.
I tried using the findIndex method to define i and then iterate it and push in into an [], using reduce
let roughArray = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
function sortArray() {
roughArray.map(num => {
if (num[i] > 1) {
roughArray.reduce((acc, num) => {
return acc.concat(num)
}, [])
}
})
sortArray()
I also tried:
const cleanArray = roughArray.reduce((acc, num) => {
let i = acc.findIndex(num);
if (i) {
return acc.concat(num);
}
}, [])
cleanArray();
I expect this in case of only numbers
[[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591]
And this in case of some included strings:
[[1,2], ["2", "3"]]
let roughArray = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
let results = {}
roughArray.map( num => {
if(results[num])
results[num].push(num)
else
results[num] = [num]
})
let bigArray = []
bigArray = Object.values(results)
let final_result = []
bigArray.map(array => {
if(array.length == 1)
final_result.push(array[0])
else
final_result.push(array)
})
console.log(final_result)
You could declare some callbacks for the various types of grouping and get the wanted type by checking the array and take an object for the grouped values.
function sortOf(array) {
const
mixed = (r, v) => {
var key = typeof v;
r[key] = r[key] || [];
r[key].push(v);
return r;
},
numbers = (r, v) => {
if (v in r) r[v] = [].concat(r[v], v);
else r[v] = v;
return r;
};
return Object.values(array.reduce(array.some(v => typeof v === 'string')
? mixed
: numbers,
Object.create(null)
));
}
console.log(sortOf([1, '1', '2', 3]));
console.log(sortOf([5, 2, 3, 3, 4, 5, 5, 1]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
First separate the strings from the rest. Sort the numbers, group them, then add the strings back in at the end.
If you want to, you can then map all the single item arrays into just single items, but that seems like it would make the output confusing.
let start = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20, '2', '3'];
let strings = start.filter(v => typeof(v) === 'string');
let notStrings = start.filter(v => typeof(v) !== 'string');
let sortedNotStrings = notStrings.sort((a,b) => a > b);
let grouped = sortedNotStrings.reduce((acc, value) =>
{
if(acc.length > 0)
{
if(acc[0][0] === value)
{
acc[0].push(value);
}
else
{
acc.unshift([value]);
}
}
else
{
acc.unshift([value]);
}
return acc;
}, []);
let sortedGrouped = grouped.sort((g1, g2) => g1[0] > g2[0]);
let withStrings = [sortedGrouped, strings];
console.log(withStrings);
let lonelySingleItems = sortedGrouped.map(arr => arr.length > 1 ? arr : arr[0]);
console.log([lonelySingleItems, strings]);
Regarding the if statement:
if(acc.length > 0)
{
if(acc[0][0] === value)
{
acc[0].push(value);
}
else
{
acc.unshift([value]);
}
}
else
{
acc.unshift([value]);
}
What I'm doing with the reduce function is passing in a default value [], so if we're at the start (i.e. the result is empty) then we put the first item in the sortedNotStrings array into the accumulating result (acc). This is what is happening in the outermost else.
If this isn't the beginning (i.e. acc is not empty) then we need to check if the value is the same as the last value added. If it is the same, put it into the array, otherwise start a new array in acc.
acc is an array of arrays, which is why [value] is being unshifted to start, rather than value.
In order to not have to access the last array of acc, I'm using unshift to put things on the front of the array. This is just to make the code look cleaner, by not using of acc[acc.length-1]. On the other hand you can do acc[acc.length-1].push([value]), and that means the grouped.sort is unnecessary, because the values won't be back to front.
If you have a really large array, eliminating the second sort is probably preferable to not having to type acc.length - 1.
Here I use an object literal {} as the accumulator for Array.prototype.reduce, in case of strings in the array I used str as the key of the object {} accumulator and added the strings as the value. So if a string is encountered the accumulator will be {str: "23"}.
In case of numbers I checked if the value is repeated or not, if repeated I created an array and added the new duplicate number to it with the key being the number itself e.g. {1: [1,1]}
Finally when the accumulator object is constructed I just take the values part of the accumulator object using Object.values which I return:
let roughArray = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20, "23", "23", "34"];
function group(roughArray) {
return Object.values(roughArray.reduce((r, e) => {
if(r['str'] && typeof e === "string"){ r['str'] = Array.isArray(r['str']) ? [...r['str']].concat(e): [r['str'], e]; }
else if(typeof e === "string"){r['str'] = [e]}
else if(r[e]){r[e] = Array.isArray(r[e]) ? [...r[e]].concat(e): [r[e], e];}
else{ r[e] = e}
return r;
}, {}));
}
console.log(group(roughArray));
Note: Array.isArray(r['str']) checks whether the value of the str key is an array if so I can use the es6 spread operator ... to get the old values of the array and also append the new one to the existing array.

How to get the sum of all duplicates in an array?

I am trying to calculate the sum of all duplicates in an array. For example:
duplicate([1,1,2,3,3]) --> should return 8.
I have written the following function to calculate the sum of duplicates in an array using JavaScript. Currently it is returning an array with duplicates one less than what they are present in the array.
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
stack.push(arr[i])
}
}
return stack;
}
console.log(duplicate([1,2,1,2,2,3,3]))
This is returning [ 1, 2, 2, 3 ]
How do I get the correct array and calculate the correct sum? I have to use Object for that?
To make the logic easier, you might filter out the non-duplicates by checking whether their indexOf in the array is equal to their lastIndexOf in the array:
function duplicate(arr) {
const duplicates = arr.filter(elm => arr.indexOf(elm) !== arr.lastIndexOf(elm));
return duplicates.reduce((a, b) => a + b);
}
console.log(duplicate([1,1,2,3,3])); // --> should return 8.
console.log(duplicate([1,2,1,2,2,3,3]));
Initially create an object where the keys will be the integer and their value will be the number of occurrence. Then if the number of occurrence is more than 1 , multiply the number with number of occurrence.
function duplicate(arr) {
let dupVal = 0;
let k = arr.reduce((acc, curr, index) => {
if (acc[curr] === undefined) {
acc[curr] = 1
} else {
acc[curr] += 1;
}
return acc
}, {});
for (let keys in k) {
if (k[keys] > 1) {
dupVal += parseInt(keys, 10) * k[keys]
}
}
return dupVal;
}
console.log(duplicate([1, 2, 1, 2, 2, 3, 3]))
Try This one
const arr = [1,1,2,3,3]
let dup = arr.filter((value, index)=>{
// creating a copy of main array
let copyarr = [].concat(arr)
// removing present value
copyarr.splice(index,1)
// after removing present value, if you still
// get the value in copied array that means
// it has duplicates
if(copyarr.indexOf(value)>-1){
return true
}
return false
})
// now add it using reduce
let sum = dup.reduce((acc, value)=> acc+value,0)
console.log(sum)
Copy above code and paste into chrome devTool. You will get the answer.
The problem is that you are matching value with immediate next value in array, in array that is sorted already it will work, but not on unsorted one. So try to sort the array first and then run your code.
Edit :
Looks like sorting is added in code,
But another condition => if there is number that is repeated more than twice it should be handled and only appear once in stack, if that is required.
This will : console.log(duplicate([1,2,1,2,2,3,3]))
Result this : [1,2,3]
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
if(stack.length == 0 || (arr[i] != stack[stack.length-1])){
stack.push(arr[i])
}
}
}
return stack;
}
you can use JS Array.reduce method to accomplish your requirement in a shorter way
function sumDplicates(arr) {
return arr.reduce(function(tot, val, index, _arr) {
if (_arr.lastIndexOf(val) > index || _arr.indexOf(val) != index)
return tot + val;
return tot
}, 0)
}
console.log(sumDplicates([1, 1, 2, 3, 3]));
console.log(sumDplicates([1, 2, 1, 2, 2, 3, 3]));
You can pursue your original sorting approach with a slight modification:
if (arr[i] === arr[i + 1] || arr[i] === arr[i - 1])
That is, check if the previous or the next element in the sorted array is equal to the current element for it to qualify as a duplicate.
The following solution accomplishes this with filter and reduce:
function duplicate(array) {
return array
.sort((a, b) => a - b)
.filter((a, i, arr) => (arr[i] === arr[i + 1] || arr[i] === arr[i - 1]))
.reduce((a, b) => a + b, 0);
}
console.log(duplicate([1, 1, 2, 3, 3]));
console.log(duplicate([1, 2, 1, 2, 3, 3]));
Array.reduce() and Array.lastIndexOf() will simply solve your problem.
function sum(arr)
{
return arr.reduce(function(sum, item){
return arr.lastIndexOf(item)!==arr.indexOf(item) ? sum+=item : sum;
},0)
}
console.log(sum([1,1,2,3,3]));
console.log(sum([1,2,3,4]));
console.log(sum([1,2,2,3,4]));
console.log(sum([1,1,2,2,3,3,4,4]));
Though I don't know much about JavaScript, If I were you, I would have simply kept a temporary array, which copies all the duplicate variables and then use that array for sum.
Also, if you want to add the particular number as many times as it appears, I will suggest creating a table like the one in sparse matrices
and then referring to it during addition.
This logic, though not space efficient, is very easy to implement.
Here is an approach with a single Array.reduce and nothing else. No Array.indexOf or Array.lastIndexOf. Although it might not be as concise it does not traverse the array looking for indexes multiple times nor does any Array.filter:
const sumDubs = arr => arr.reduce((r,c) => {
if(r[c]) {
r[c] += 1
r.sum += r[c] > 2 ? (r[c]*c) - ((r[c]-1)*c) : r[c]*c
} else r[c] = 1
return r
}, { sum: 0 }).sum
console.log(sumDubs([1, 1, 2, 3, 3])) // 8
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3])) // 14
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3, 3, 1, 2, 4, 4])) // 28
The idea is to keep track of the on-going sum via a property in the accumulator of the Array.reduce and simply keep calculating the sum based on which number is duplicated and more importantly how many times.

What is the most elegant way to insert objects between array elements?

I'm sure there are many ways to achieve that but I'm looking for something "elegant".
a = [
'a',
'b',
'c'
];
magicArrayJoin(a, {value: 255} ); // insert the same object between each item
result == [
'a',
{value: 255},
'b',
{value: 255}
'c'
];
All proposals are welcome. :)
One-liner using plain ES6:
const interleave = (arr, thing) => [].concat(...arr.map(n => [n, thing])).slice(0, -1)
Usage:
interleave(['foo', 'bar', 'baz'], 'avocado')
Prints:
> ["foo", "avocado", "bar", "avocado", "baz"]
You can do it with flatMap. It can be found from lodash for example
_.flatMap([1,2,3,4], (value, index, array) =>
array.length -1 !== index // check for the last item
? [value, "s"]
: value
);
ouputs
[1, "s", 2, "s", 3, "s", 4]
Update
Array#flatMap proposal is in the works so in future this should work:
[1, 2, 3, 4].flatMap(
(value, index, array) =>
array.length - 1 !== index // check for the last item
? [value, "s"]
: value,
);
In my opinion the most elegant way to do this is the following one:
ES6 syntax version
const insertIntoArray = (arr, value) => {
return arr.reduce((result, element, index, array) => {
result.push(element);
if (index < array.length - 1) {
result.push(value);
}
return result;
}, []);
};
Usage:
insertIntoArray([1, 2, 3], 'x'); // => [1, 'x', 2, 'x', 3]
An ordinary loop seems to be the best:
function intersperse(arr, el) {
var res = [], i=0;
if (i < arr.length)
res.push(arr[i++]);
while (i < arr.length)
res.push(el, arr[i++]);
return res;
}
If you're looking for something elegant, it would probably have to use some kind of concatMap, as in
function concatMap(arr, fn) { return [].concat.apply([], arr.map(fn)); }
function intersperse(arr, el) { return concatMap(arr, x => [el, x]).slice(1); }
Use ES6 flatMap function.
const insertBetween = (ele, array) => {
return array.flatMap((x) => [ele, x]).slice(1);
};
insertBetween('+', [1, 2, 3]);
Immutable solution
When reducing an array the reduce function should not mutate the array but return a new value (in this case a new array). That way the changes will be only applied to the returned array and not the original one and side effects will be avoided.
const insertBetween = (insertee, array) => array.reduce(
(acc, item, i, { length }) => {
if (i && i < length) {
return [...acc, insertee, item];
}
return [...acc, item];
},
[]
);
Ramda has intersperse method that:
Creates a new list with the separator interposed between elements.
Code:
R.intersperse({name: 'separator'}, ['one', 'two', 'three']);
Result:
[
'one',
{name: 'separator'},
'two',
{name: 'separator'},
'three'
]
This worked for me:
a.map(val = [val, {value: 255}]).flat()
You can achieve this using reduce (it is also immutable).
const insertBetween = (insertion, array) =>
array.reduce(
(newArray, member, i, array) =>
i < array.length - 1
? newArray.concat(member, insertion)
: newArray.concat(member),
[]
);
const result = insertBetween('and', [1, 2, 3]);
console.log(result);
// outputs;
// [
// 1,
// 'and',
// 2,
// 'and',
// 3
// ]
Or in older JS syntax;
function insertBetween(insertion, array) {
const indexOfLastItem = array.length - 1;
return array.reduce(withInsertion, []);
function withInsertion(newArray, item, index, array) {
return index < indexOfLastItem
? newArray.concat(item, insertion)
: newArray.concat(item);
}
}
const result = insertBetween('and', [1, 2, 3]);
console.log(result);
// outputs;
// [
// 1,
// 'and',
// 2,
// 'and',
// 3
// ]
I really in favor of #Vidul 's comments, which is very logical and concise! I myself also came up with splice(), but missed %. However, most of the braces seem unnecessary as an oneliner. It can be further simplified as
for (var i = 0; i < a.length; i++) if (i % 2) a.splice(i, 0, {value: 255});
function insertObject(arr, obj) {
var result = [];
function insert(element, index) {
result.push(element);
if (index + 1 < arr.length) {
result.push(obj);
}
}
arr.forEach(insert);
return result;
}
var a = [1, 2, 3, 4];
insertObject(a, {
test: 'test'
});
Using splice as Kamen suggests, you could do something like:
const numSeparators = arr.length - 1;
for (let i = 1; i <= numSeparators; i++) {
const index = (i * 2) - 1;
arr.splice(index, 0, { value: 255 });
}
for a simple purely functional way I suggest doing it this way:
const magicArrayJoin = (array, el) =>
array.length ?
array.slice(1).reduce((acc, cur) => acc.concat([el, cur]), [array[0]]) :
[]
p.n. this way is not the most performant one in javascript
ES6:
const arrayWithSeparator = array.reduce((a, i) => a.length ? a.push(separator) && a.push(i) && a : a.push(u) && a, [])
Array.splice() should do the job like so:
a.splice(1, 0, {value : 255})
The first argument is the position at which you want to delete or insert elements, the second is the delete count, the third (optional) is the new element[s] you want to insert.

Categories

Resources