Can I have an object key as part of an array? - javascript

I am trying to sort some numbers, and I want to count the number of times that an array has a certain number.
My question is more about the structure of an the array than the counting the number part. I would like to build an array that looks like this below.
let numbers = [1,2,3,4,4,4,5,5,6];
How could I make the data structure below?
numbers[3].count // this will be equal to 3 after I loop through;
How do I make each part of the array have an object parameter?
Do I just loop through like so?
for (let i = 0; i < numbers.length; i++){
numbers[i] = {
count: 0
}
}
I understand this wont give me the right count, but I don't care about that part of the problem. I would like to solve that on my own. I just need to be sure that this is the correct way to add the object parameters.

I would build these functions on my own. Something like this
You can copy and paste this in the console of your browser.
// my numbers list
const numbers = [1, 2, 3, 4, 4, 4, 5, 5, 6];
// reduced to unique entries
const uniques = [...new Set(numbers)];
// function to count occurrences in my list of number
const count = (n) => numbers.filter((num) => num === n).length;
// you can test here
console.log(`counting ${uniques[4]}s`, count(uniques[4]));
// get these as object
console.log(uniques.map((unique) => ({[unique]: count(unique)})))

Simplest way to achieve this by using Array.forEach().
let numbers = [1,2,3,4,4,4,5,5,6];
const obj = {};
numbers.forEach((item) => {
obj[item] = (obj[item] || 0) + 1;
});
console.log(obj);

const numbers = [1,2,3,4,4,4,5,5,6];
const counts = {};
numbers.forEach((x) => counts[x] = (counts[x] || 0) + 1);
console.log(counts)

You can use the Array#reduce() method to group elements together into sub arrays. Since arrays have a length property that gives the the number of elements this can be applied to each group of like elements. We do not need to create a new count property.
let numbers = [1,2,3,4,4,4,5,5,6];
const freq = numbers.reduce(
(acc,cur) => ({...acc,[cur]:(acc[cur] || []).concat(cur)})
);
console.log( freq[4] );
console.log( freq[4].length );
Alternatively, you can put the numbers in an object and get all the unique elements, then for each unique element define a get property that groups like elements together using the Array#filter() method. Again, the length array property can be used to return the number of elements for each unique element.
const o = {numbers: [1,2,3,4,4,4,5,5,6]};
o.uniq = [...new Set(o.numbers)];
o.uniq.forEach(n => Object.defineProperty(
o,n,{get:() => o.numbers.filter(num => num === n)}
));
console.log( o[5] );
console.log( o[5].length );

Reduce is perfect for these kinds of problems.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = 1;
} else {
tmpObj[number] += 1;
}
return tmpObj
}, {});
console.log(countedObject);
if you feel the need to nest it further you can of course do this.
But if count is the only property you need, I'd suggest sticking to the first version.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = {count: 1};
} else {
tmpObj[number].count += 1;
}
return tmpObj
}, {});
console.log(countedObject);

Related

what's the most efficient way to split an array of millions of data based on condition?

It goes something like this where I have a london array containing more than 10 million data
london = ['dwig7xmW','gIzbnHNI' ...]
And now I have a userTraveled which also contains millions of data
userTraveled = ['ntuJV09a' ...]
Now what's the most efficient way to split userTraveled into inLondon and notInLondon.
My attempt.
inLondon = []
notInLondon = []
userTraveled.forEach((p) => london.includes(p) ? inLondon.push(p) : notInLondon.push(p))
london.includes(p) will do a linear search over the array. Doing that for every userTraveled is horribly inefficient. Use a Set instead:
const usersInLondon = [], usersNotInLondon = [];
const lookup = new Set(london);
for (const p of usersTraveled) {
(lookup.has(p) ? usersInLondon : usersNotInLondon).push(p);
}
I can offer a O(n*log(n)) solution instead of your O(n^2), first order the passwords and later use the binary search on it instead of the include to search for an item
Hope it helps =)
const london = ['dwig7xmW','gIzbnHNI']
const userTraveled = ['ntuJV09a', 'dwig7xmW']
let inLondon = []
let notInLondon = []
const sortedlondon=london.sort();
userTraveled.forEach((p) => (binarySearch(sortedlondon,p)!=-1 ? inLondon.push(p) : notInLondon.push(p)))
//https://www.htmlgoodies.com/javascript/how-to-search-a-javascript-string-array-using-a-binary-search/
function binarySearch(items, value){
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
while(items[middle] != value && startIndex < stopIndex){
//adjust search area
if (value < items[middle]){
stopIndex = middle - 1;
} else if (value > items[middle]){
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex)/2);
}
//make sure it's the right value
return (items[middle] != value) ? -1 : middle;
}
I hope you are not using these data in a wrong way.
const passwords = ['a', 'b']
const rawPasswords = ['c', 'b'];
const setPasswords = new Set(passwords)
const uniquePassword = [];
const usedPassword = [];
rawPasswords.forEach(rp => {
if (setPasswords.has(rp)) {
usedPassword.push(rp)
} else {
uniquePassword.push(rp)
}
})
console.log(uniquePassword, usedPassword)
Referring to this answer for performance tests: Get all unique values in a JavaScript array (remove duplicates) the best solution in your case would be to use an Object. Since you require to know about the duplicates and not just remove them.
function uniqueArray( ar ) {
var j = {};
var k = [];
var unique;
ar.forEach( function(v) {
if(j.hasOwnProperty(v)){
k.push(v);
} else {
j[v] = v;
}
});
unique = Object.keys(j).map(function(v){
return j[v];
});
return [unique, k];
}
var arr = [1, 1, 2, 3, 4, 5, 4, 3];
console.log(uniqueArray(arr));
First it loops through the input array and checks if the value is already existing as a key on the object. If that's not the case, it adds it. If it is, it pushes the value to another array. Since objects use a hash, the Javascript engine can work faster with it.
Secondly it goes through the object's keys to turn it back into an array and finally returns both. I didn't add this explanation because the provided reference already explained it.
The result will be an array containing 2 arrays. First the array with unique values, second the array with duplicates.

How to adjust return values of map() function?

I have been trying to make a excercise in the course I am taking. At the end, I did what was asked, but I personally think I overdid too much and the output is not convenient -- it's a nested array with some blank arrays inside...
I tried to play with return, but then figured out the problem was in the function I used: map always returns an array. But all other functions, which are acceptable for arrays (in paticular forEach and I even tried filter) are not giving the output at all, only undefined. So, in the end, I have to ask you how to make code more clean with normal output like array with just 2 needed numbers in it (I can only think of complex way to fix this and it'll add unneeded junk to the code).
Information
Task:
Write a javascript function that takes an array of numbers and a target number. The function should find two different numbers in the array that, when added together, give the target number. For example: answer([1,2,3], 4) should return [1,3]
Code
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed
})
return correctNumbers;
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
Output
[[],[5],[3]]
for the first one
You can clean up the outpu by flatting the returned arrays :
return arrayWeNeed.flat();
and
return correctNumbers.flat();
const array1 = [1, 2, 3];
const easierArray = [1, 3, 5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = ((arr, targetNum) => {
const correctNumbers = arr.map((num, index) => {
let firstNumber = num;
// console.log('num',num,'index',index);
const arrayWeNeed = arr.filter((sub_num, sub_index) => {
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum) {
const passableArray = [firstNumber, sub_num] //aka first and second numbers that give the targetNum
return sub_num; //passableArray gives the same output for some reason,it doesn't really matter.
}
})
return arrayWeNeed.flat();
})
return correctNumbers.flat();
// return `there is no such numbers,that give ${targetNum}`;
})
console.log(findTwoPartsOfTheNumber(easierArray, 8));
console.log(findTwoPartsOfTheNumber(array1, 4));
However, using a recursive function could be simpler :
const answer = (arr, num) => {
if (arr.length < 1) return;
const [first, ...rest] = arr.sort();
for (let i = 0; i < rest.length; i++) {
if (first + rest[i] === num) return [first, rest[i]];
}
return answer(rest, num);
};
console.log(answer([1, 2, 3], 4));
console.log(answer([1, 3, 5], 8));
It looks like you are trying to leave .map() and .filter() beforehand, which you can't (without throwing an error). So I suggest a normal for approach for this kind of implementation:
const array1 = [1,2,3];
const easierArray = [1,3,5] //Let's assume number we search what is the sum of 8
const findTwoPartsOfTheNumber = (arr,targetNum) =>{
for(let index = 0; index < arr.length; index++) {
let firstNumber = arr[index];
// console.log('num',num,'index',index);
for(let sub_index = 0; sub_index < arr.length; sub_index++){
const sub_num = arr[sub_index];
// console.log('sub_num',sub_num,'sub_index',sub_index);
if (index != sub_index && (firstNumber + sub_num) === targetNum){
const passableArray = [firstNumber,sub_num]//aka first and second numbers that give the targetNum
return passableArray; //passableArray gives the same output for some reason,it doesn't really matter.
}
}
}
return `there is no such numbers,that give ${targetNum}`;
}
console.log(findTwoPartsOfTheNumber(easierArray,8));
console.log(findTwoPartsOfTheNumber(array1,4));
console.log(findTwoPartsOfTheNumber(array1,10));
I've just grab your code and changed map and filter to for implementation.
There doesn't appear to be any requirement for using specific array functions (map, forEach, filter, etc) in the problem statement you listed, so the code can be greatly simplified by using a while loop and the fact that you know that the second number has to be equal to target - first (since the requirement is first + second == target that means second == target - first). The problem statement also doesn't say what to do if no numbers are found, so you could either return an empty array or some other value, or even throw an error.
const answer = (list, target) => {
while (list.length > 0) { // Loop until the list no longer has any items
let first = list.shift() // Take the first number from the list
let second = target - first // Calculate what the second number should be
if (list.includes(second)) { // Check to see if the second number is in the remaining list
return [first, second] // If it is, we're done -- return them
}
}
return "No valid numbers found" // We made it through the entire list without finding a match
}
console.log(answer([1,2,3], 3))
console.log(answer([1,2,3], 4))
console.log(answer([1,2,3], 7))
You can also add all the values in the array to find the total, and subtract the total by the target to find the value you need to remove from the array. That will then give you an array with values that add up to the total.
let arr1 = [1, 3, 5]
const target = 6
const example = (arr, target) => {
let total = arr.reduce((num1, num2) => {
return num1 + num2
})
total = total - target
const index = arr.indexOf(total)
if (index > -1) {
return arr.filter(item => item !== total)
}
}
console.log(example(arr1, target))
Map and filter are nice functions to have if you know that you need to loop into the whole array. In your case this is not necessary.
So you know you need to find two numbers, let's say X,Y, which belong to an array A and once added will give you the target number T.
Since it's an exercise, I don't want to give you the working code, but here is a few hints:
If you know X, Y must be T - X. So you need to verify that T - X exists in your array.
array.indexOf() give you the position of an element in an array, otherwise -1
If X and Y are the same number, you need to ensure that their index are not the same, otherwise you'll return X twice
Returning the solution should be simple as return [X,Y]
So this can be simplified with a for (let i = 0; i < arr.length; i++) loop and a if statement with a return inside if the solution exist. This way, if a solution is found, the function won't loop further.
After that loop, you return [] because no solution were found.
EDIT:
Since you want a solution with map and filter:
findTwoPartsOfTheNumber = (arr, tNumber) => {
let solution = [];
arr.map((X, indexOfX) => {
const results = arr.filter((Y, indexOfY) => {
const add = Y + X
if (tNumber === add && indexOfX != indexOfY) return true;
else return false;
});
if (results > 0) solution = [X, results[0]];
})
return solution;
}

in the easiest and most concise way as possible

I want to sort an array values in an ascending or descending order without using sort().
I have created a function, however I am not satisfied with it.
I believe the code below could be much shorter and more concise.
Please let me know where to modify or you may entirely change the code too. Thank you in advance.
const func = arg => {
let flip = false;
let copy = [];
for(let val of arg) copy[copy.length] = val;
for(let i=0; i<arg.length; i++) {
const previous = arg[i-1];
const current = arg[i];
if(previous > current) {
flip = true;
copy[i] = previous;
copy[i-1] = current;
}
}
if(flip) return func(copy);
return copy;
};
l(func([5,2,8,1,9,4,7,3,6]));
If your input is composed of whole numbers, as in the example, pne option is to reduce the array into an object, whose keys are the numbers, and whose values are the number of times those values have occured so far. Then, iterate over the object (whose Object.entries will iterate in ascending numeric key order, for whole number keys), and create the array to return:
const func = arr => {
const valuesObj = {};
arr.forEach((num) => {
valuesObj[num] = (valuesObj[num] || 0) + 1;
});
return Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
);
};
console.log(
func([5,2,8,1,9,10,10,11,4,7,3,6])
);
This runs in O(N) time.
To account for negative integers as well while keeping O(N) runtime, create another object for negatives:
const func = arr => {
const valuesObj = {};
const negativeValuesObj = {};
arr.forEach((num) => {
if (num >= 0) valuesObj[num] = (valuesObj[num] || 0) + 1;
else negativeValuesObj[-num] = (negativeValuesObj[-num] || 0) + 1;
});
return [
...Object.entries(negativeValuesObj).reverse()
.flatMap(
([num, count]) => Array(count).fill(-num)
),
...Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
)
];
};
console.log(
func([5,2,8,1,-5, -1, 9,10,10,11,4,7,3,6, -10])
);
For non-integer items, you'll have to use a different algorithm with higher computational complexity.

How to remove certain elements from an array into a new array and leave the others only the original array?

How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);

Return unique elements from two arrays

I have two arrays of numbers I want get get the unique numbers that appears in both arrays. Then I want to also return the unique numbers from both arrays.
For example:
INPUT:
let arr1 = [1234,4056,3045]
let arr2 = [5678,1234,5001]
OUTPUT:
only in arr1: [4056, 3045]
only in arr2: [5678, 5001]
in both lists: [1234]
Here is my solution, it works but I can't think of how optimize my solution. Just using JavaScript, no tools like loadash. Any thoughts?:
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter((ele) => {
return arr2.indexOf(ele) !== -1
})
let uniqueOfList1 = arr1.filter((ele) => {
return arr2.indexOf(ele) == -1
})
let uniqueOfList2 = arr2.filter((ele) => {
return arr1.indexOf(ele) == -1
})
return `Unique numbers from both list are ${uniqueOfBoth}
Unique nums to List1 : ${uniqueOfList1}
Unique nums to List2 : ${uniqueOfList2}
`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
I think this approach is fine so long as it doesn't become a bottle neck. You are doing three O(n**2) operations to get your lists, so it could be nice if there was a way to reduce the complexity.
One thing you could try is to use a hash table that keeps count of how many times the numbers are seen. But you need to be a little clever because you can't just count otherwise you wouldn't know if 1 means arr1 or arr2. But since there are only 4 possibilities you only need 2 bits to represent them. So you add 1 when it's in array1 and 2 when it's in array1. That means 1 in is arr1, 2 in arr2, and 3 is in both. Creating the counts is only O(n+m) where n and m are the array lengths. (You still need to filter that, however, to get your final result)
const getUniqueNumbers =(arr1,arr2) =>{
let counter = {}
arr1.forEach(i => counter[i] = counter[i] ? counter[i] + 1 : 1)
arr2.forEach(i => counter[i] = counter[i] ? counter[i] + 2 : 2)
return counter
}
let counts = getUniqueNumbers([1234,4056,3045],[5678,1234,5001])
console.log(counts)
Then it's just a matter of filtering what you want with something like:
let both = Object.keys(counter).filter(key => result[key] === 3)
You could use Array#includes instead of Array#indexOf, because it returns a boolean value instead of the index.
For getting a difference, you could filter by the unique values of both arrays (this yields a smaller set, than to take the original arrays).
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter(ele => arr2.includes(ele))
let uniqueOfList1 = arr1.filter((ele) => !uniqueOfBoth.includes(ele))
let uniqueOfList2 = arr2.filter((ele) => !uniqueOfBoth.includes(ele))
return `Unique numbers from both list are ${uniqueOfBoth}\nUnique nums to List1 : ${uniqueOfList1}\nUnique nums to List2 : ${uniqueOfList2}`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
Here's another version.
This solution assumes the arrays are of equal length. We first iterate through the arrays and store the values in 2 dictionaries. This eliminates any duplicate integers found in the same array. We then iterate over one of the dictionaries and check if the key is found in both, then delete that key from both. Finally, we get the remaining keys from both dictionaries and store them as arrays.
const fn = (arr1, arr2) => {
const obj = {
arr1: [],
arr2: [],
both: []
};
const dict1 = {};
const dict2 = {};
for (let i = arr1.length; i--;) {
dict1[arr1[i]] = true;
dict2[arr2[i]] = true;
}
for (let key in dict1) {
if (key in dict2) {
obj.both.push(key);
delete dict1[key];
delete dict2[key];
}
}
obj.arr1 = Object.keys(dict1);
obj.arr2 = Object.keys(dict2);
return obj;
}
const arr1 = [1234, 4056, 3045];
const arr2 = [5678, 1234, 5001];
console.log(fn(arr1, arr2));

Categories

Resources