Function to create array of arrays in javascript - javascript

Given an array with characters such as ["A","P","P","L","E","S","A","R","E"], I'm trying to create a function that will loop over the elements in the array, and create an array for each character, which will then be put into a master array. The master array will end up looking like[["A"],["P","P"],["L"],["E"],["S"],["A"],["R"],["E"] at the end, using a comparator function to check values (a,b) => a == b. Essentially, it needs to check each successive letter, and if they are the same, group into their own array within the master. The two A's should not be grouped together, since they aren't successive.
var arr = ["A","P","P","L","E"];
var master = [];
arr.sort(function(a,b){
for(var i = 0; i <= arr.length; i++){
compare each element to its successor. if successor is the same, create array of like elements ["A"],["C","C"],["B"] within final array
if(i + 1 == i){
master.push(i);
}
}
});

Just loop through the array and compare the last value to the current one.
DO NOT SORT -- that will change the order of your input array!
const coolFn = (arr) => {
return arr.reduce((rez, value, index) => {
if (index !== 0 && rez[rez.length - 1][0] === value) {
rez[rez.length - 1].push(value);
} else {
rez.push([value]);
}
return rez;
}, []);
}
const rez = coolFn('APPLES ARE NOT A BANANA PUDDING CUP'.split(''));
console.log(rez);

Can be accomplished pretty easily with Set to get unique items, reduce to turn transform and filter to find matching elements:
const arr = ["A","P","P","L","E"]
// get unique keys by expanding to a Set
const letters = [...new Set(arr)].reduce((p, c) => {
// add all matching elements from original array to aggregate
p.push(arr.filter(i => i === c))
return p;
}, []);
console.log(letters);
edit: sorry, I missed the requirement (hidden in a comment in your code) that you only add by comparing each element to its successor. My solution creates an array of each letter with its number of occurrences

You might do as follows;
var arr = ["A","P","P","L","E"],
result = arr.reduce((p,c) => {var fi = p.findIndex(a => a[0] === c);
fi === -1 ? p.push([c]) : p[fi].push(c);
return p;
},[]);
console.log(result);
As per the grouping only the sequential duplicates the following should do;
var arr = ["A","P","P","L","E","S","A","R","E"],
stickTheSame = a => a.reduce((p,c) => (p[p.length-1][0] === void 0 ||
p[p.length-1][0] === c ? p[p.length-1].push(c)
: p.push([c]),
p),[[]]);
console.log(JSON.stringify(stickTheSame(arr)));

Related

Write a function getDuplicates

Write a function getDuplicates that returns an array of all the elements that appear more than once in the initial items array (keeping the order). If an element appears many times, it should still be added to the result once.
This is my code
function getDuplicates(items) {
let result = [];
if (items === [0,0,0,0]) {return [0]}
for (let i = 0; i < items.length; i++) {
for (let j = i + 1; j < items.length; j++) {
if (items[i] === items[j]) {
result.push(items[i])
}
}
}
return result
}
I get an error:
input: [0, 0, 0, 0]
Hide details
Expected:
[0]
Received:
[0,0,0,0,0,0]
In JavaScript, arrays are objects, so when you use the === operator to compare two arrays, it will only return true if they are the exact same object in memory.
Use a Set to track duplicates: Instead of using an array to store the duplicate elements, we can use a Set to make sure we don't add duplicates to the result array. A Set is an efficient data structure for checking if an element exists or not, and it also automatically removes duplicates.
Use a single loop: Instead of using two nested loops to compare every element with every other element, we can use a single loop to keep track of the elements we've seen so far, and add them to the result if we see them again.
function getDuplicates(items) {
const result = [];
const seen = new Set();
for (const item of items) {
if (seen.has(item) && !result.includes(item)) {
result.push(item);
} else {
seen.add(item);
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
a modified version of yours
function getDuplicates(items) {
let result = [];
let added = {};
for (let i = 0; i < items.length; i++) {
if (!added[items[i]] && items.indexOf(items[i], i + 1) !== -1) {
result.push(items[i]);
added[items[i]] = true;
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
or in short doing the same
const getDuplicates = items => items.filter((item, index) => items.indexOf(item) !== index && items.lastIndexOf(item) === index);
console.log(getDuplicates([0, 1, 0, 1, 2]))
The best way to filter out the unique elements in an array is JavaScript Set
You cannot compare two arrays just like array1 === array2 because, Arrays have the type Object and you cannot compare two object just with equal to operator. Objects are not compared based on their values but based on the references of the variables. So when you compare two arrays which have same values using array1 === array2, it will compare its memory location only, not its values. So it will be only false.
The best way to achieve your result is to create an Array by checking the number of occurrences of nodes in the parent array, having occurrences count more than one and use a Set to remove the repetitions
function getDuplicates(items) {
return Array.from(new Set(items.filter(node => items.filter(x => node === x).length > 1)))
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
You can try it:
Check if the current number is duplicated by using filter to check the length of an array.
Check if the result array contains duplicates.
function getDuplicates(items) {
let result = [];
for (let i = 0; i < items.length; i++) {
if ((items.filter(item => item == items[i])).length > 1 && !result.includes(items[i])) {
result.push(items[i]);
}
}
return result;
}
console.log(getDuplicates([0, 0, 0, 0]));
So. first of all - comparing 2 array will not work, (Somebody already explained why above).
Your code doesn't work because of if statement. You're checking if an array doesn't have any value except 0.
Try summing all numbers in the array and check if it's 0.
if(arr.reduce((accum, curr) => return accum += curr) == 0) {
return [0];
}
Your code is close, but there are a few issues that need to be addressed. First, you should not use the strict equality operator === to compare arrays, because it checks whether the two arrays have the same reference, not the same elements. Instead, you can use the JSON.stringify() method to compare the string representations of the arrays.
Second, your code only returns [0] if the input array is [0,0,0,0], which is not a general solution for finding duplicates. You can use an object to keep track of the count of each element in the array, and then add the elements that have a count greater than 1 to the result array.
Here's the corrected code:
function getDuplicates(items) {
let result = [];
let count = {};
for (let i = 0; i < items.length; i++) {
if (count[items[i]] === undefined) {
count[items[i]] = 1;
} else {
count[items[i]]++;
}
}
for (let i = 0; i < items.length; i++) {
if (count[items[i]] > 1 && result.indexOf(items[i]) === -1) {
result.push(items[i]);
}
}
return result;
}
This code keeps track of the count of each element in the count object, and then adds the elements that have a count greater than 1 to the result array, while making sure not to add duplicates to the result.

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

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

Is there a way to filter an array with specific number of returned items in JS?

For example, I only want to get two items that satisfy the filter condition.
filter() then slice(2) will iterate all items which is really a waste while find() can only return one item.
Is there any way to specify the number of returned items that satisfy the filter condition? except writing for loop by myself.
reduce should do the job, consider the following example
[1, 2, 3, 4].reduce((acc, n) => n > 2 ? [...acc, n] : acc, []) // results [3, 4]
You can just loop and break. You could make a helper method to do this:
const found = [];
for (let i = 0; i < sourceArray.length; i++) {
if (whatever) found.push(sourceArray[i];
if (found.length >= 2) break;
}
I would write a helper function that takes in your array, a count number, and a predicate function, similar to how array.filter works. Return true if the item should be included.
function filterCount(array, count, predicate) {
const elements = [];
for (const el in array) {
if (predicate(el)) {
elements.push(el);
}
if (elements.length >= count) {
break;
}
}
return elements;
}
const test = [1,2,3,4,5,6,7,8,9,10];
const onlyTwo = filterCount(test, 2, (number) => number >= 5);
console.log(onlyTwo)

Filter array in javascript

I have an array which including all of the times as below :
["14:00-14:30" , "14:30-15:00", "15:00-15:30"]
as you can see this time slots, so basically, its from : 14:00 to 15:30
output will be : ["14:00-15:30"]
But if i have :
["14:00-14:30", "14:30-15:00", "15:30-16:00"]
in this case, the output would be : ["14:00-15:00", "15:30-16:00"]
My solution: convert all of this to a single array ["14:00", "14:30", "14:30", "15:00", ...]. And then forEach to each element, delete the one the have arr[i] === arr[i+1].
I got it working but I don't really like the way its. is there any better idea or how to use filter in this case ? Thanks.
Assuming the format of the array is correct:
const yourArray = ["14:00-14:30", "14:30-15:00", "15:30-16:00"];
const solution = yourArray.sort().reduce(
(acc, item, index) => {
if (index===0) {
acc.push(item);
return acc;
}
const currentValueParsed = acc[acc.length-1].split('-');
const newValueParsed = item.split('-');
if (currentValueParsed[1] === newValueParsed[0]) {
acc[acc.length-1] = `${currentValueParsed[0]}-${newValueParsed[1]}`;
return acc;
}
acc.push(item);
return acc;
}, []
);
console.log(solution); // ["14:00-15:00", "15:30-16:00"]
The code could be small, but I prefer being explicit.
We sort the array.
We add the first element to the final array.
And every new element, ee decide if we need to modify the last element of the solution array or add the new element to this array.
And because you look interested in evolve your original solution.
const yourArray = ["14:00-14:30", "14:30-15:00", "15:30-16:00"];
const solution = yourArray.sort()
.join('-')
.split('-')
.filter((item, pos, arr) => {
return pos === 0 || (item !== arr[pos - 1] && item !== arr[pos + 1]);
})
.reduce((acc, item, pos, arr) => {
if (pos % 2) {
acc.push(`${arr[pos - 1]}-${arr[pos]}`);
}
return acc;
}, []);
console.log(solution); // ["14:00-15:00", "15:30-16:00"]
Notes:
It is important to sort in the beginning.
pos % 2 is telling me if it is an even position.
I don't care arr[pos + 1] return undefined in the last item.
Filter won't work, since you're creating a new value, not just keeping existing values. Reduce would, though.
let start = null, end = null;
let finalAnswer = arr.reduce((result, current, i) => {
const [first, last] = current.split('-');
if (start === null) { start = first;}
if (first !== end && end !== null) { result.push(`${start}-${end}`); if (i === arr.length - 1) { result.push(current); }}
else if (i === arr.length - 1) { result.push(`${start}-${last}`); }
else { end = last; }
return result;
}, []);
I'm sure there's a cleaner way to do this -- I had to throw in edge cases more than I'd like -- but this works :)
The idea is that you keep track of the interval's start and end times; if the current interval's start equals the last interval's end, then update the end time to the current interval's. Otherwise, push the current start and end time and reset the counter for the next entry. The edge cases are to handle when the final entry either does or does not create its own new interval; if it does, push the entry as its own interval, and if not, push a new interval with the current start and the entry's end time.
this is really a reduce operation, so a solution could like:
const result = array
.sort() // if needed?
.map(tf => tf.split('-')) // make it easier to work with
.reduce((acc, currFrame, idx, arr) => {
let reducedFrame = acc[acc.length - 1] // get latest reduced frame
if (!reducedFrame || reducedFrame.length === 2) { // filled range or at start
reducedFrame = [currFrame[0]] // so start a new one
acc.push(reducedFrame)
}
const nextFrame = arr[idx + 1]
if (!nextFrame || nextFrame[0] !== currFrame[1]) { // at last frame or end of the current continuous frame
reducedFrame.push(currFrame[1]) // so end the reduced frame
}
return acc
}, [])
.map(tf => tf.join('-')) // put it back
or the dupe filter approach would work as well I believe, building off #Dalorzo:
const result = array
.join('-').split('-') // convert to array of singles
.filter((v,i) => array.indexOf(v) === i) // lose the dupes
.sort() // if needed (performs better here in this case)
.reduce((acc, cur, i, arr) => // join every 2
(i % 2 === 0)
? acc.concat([cur + '-' + arr[i + 1]])
: acc, [])
In order to create the desired array what I would do is to join and split like:
var arr =['14:00-14:30', '14:30-15:00', '15:30-16:00'];
var arr = arr.join('-').split('-');
The above will produce the array with all times and then you could remove the duplicates any way you want one way:
var result = arr.filter((v,i) => arr.indexOf(v) === i);

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

Categories

Resources