search an array for duplicates, javascript [duplicate] - javascript

This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 6 years ago.
Okay I'm trying to search an array and find the duplicates and return the number of times each of the duplicates occurs. This is what I have so far, I need to pass in two arguments first the array being searched and then a specific term within that array:
countMatchingElements = function(arr, searchTerm){
var count = 0;
for(i = 0; i <= arr.length; i++){
count++;
}
return count;
};
Array I want to search:
var arrayToSearch = ['apple','orange','pear','orange','orange','pear'];

var arrayToSearch = ['apple', 'orange', 'pear', 'orange', 'orange', 'pear'];
var counter = {};
arrayToSearch.forEach(function(e) {
if (!counter[e]) {
counter[e] = 1;
} else {
counter[e] += 1
}
});
console.log(counter); //{ apple: 1, orange: 3, pear: 2 }

Something like this might do the trick:
var arrayToSearch = ['apple', 'orange', 'pear', 'orange', 'orange', 'pear'];
countMatchingElements = function(arr, searchTerm) {
return arr.filter(function(item) { return item === searchTerm; }).length;
};
document.writeln('"orange" appears ' + countMatchingElements(arrayToSearch, 'orange') + ' times.');

Related

Algorithm Challenge: Fuzzy Search

I recently took part in an algorithm challenge to create a Fuzzy search with the following criteria:
Given a set array, create a function that receives one argument and returns a new array containing only the values that start with either:
A) The argument provided
B) The argument provided but with 1 difference (i.e. 1 incorrect letter)
The array was: fruits = [apple, apricot, banana, pear, mango, cherry, tomato]
so:
fuzzySearch('ap') = ['apple, apricot']
fuzzySearch('app') = ['apple', 'apricot']
fuzzySearch('appl') = ['apple']
fuzzySearch('pa') = ['banana', 'mango']
This is the solution I came up with:
const fruits = ['apple', 'apricot', 'banana', 'pear', 'mango', 'cherry', 'tomato']
function fuzzySearch(str) {
return fruits.filter(fruit =>
{
let letterCount = 0
const fruitLetArr = fruit.toLowerCase().split('')
const strArr = str.toLowerCase().split('')
for (var i = 0; i < strArr.length; i++) {
console.log(fruitLetArr[i], strArr[i], i, letterCount)
if (fruitLetArr[i] !== strArr[i]) letterCount++
if (letterCount === 2) break;
}
if (letterCount < 2) return true
});
}
fuzzySearch(str)
Can anyone think of a faster way that doesn't involve iterating over every value before a soltion can be found?
Here's something that should be slightly more efficient. Also easier to read. In this solution, I am assuming that by "difference" you mean a substitution of a letter for another letter, rather than the addition of another letter.
const fruits = ['apple', 'apricot', 'banana', 'pear', 'mango', 'cherry', 'tomato'];
const fuzzySearch = (str) => {
return fruits.filter((fruit) => {
// If our first case is met, immediately return
if (fruit.startsWith(str)) return true;
// Split the fruit based on the length of input string
const test = fruit.slice(0, str.length).split('');
let diffs = 0;
// Compare + keep track of differences between input + sliced fruit
test.forEach((letter, i) => letter !== str[i] && diffs++);
// If we have more than one difference, it doesn't meet case #2
if (diffs > 1) return false;
return true;
});
};
const testCases = ['ap', 'app', 'appl', 'pan', 'bp'];
for (const testCase of testCases) {
console.log(fuzzySearch(testCase));
}

get previous item seen in JS for of loop?

in my for element of array loop, I want to access the elements besides the current one. Specifically, the previous or next element. I would also like this to reach across the first/last element barrier. How can this be achieved?
ie. given:
my_fruits = ['apple', 'banana', 'grapes', 'blueberries']
for (const fruit of my_fruits) {
// When fruit === 'banana', I want to access 'apple',
// when fruit === 'blueberries' I want to access 'grapes'.
// Also when it's the last element, I want to acknowledge that and access the first.
}
Due to how I'm dealing with async/await in this local, .forEach is something I'd prefer to avoid.
Thanks
You should use forEach loop and use the second parameter which is index.
const arr = ['apple', 'banana', 'grapes', 'blueberries']
arr.forEach((x,i) => {
let prev = i > 0 ? arr[i - 1] : null;
let next = i < arr.length ? arr[i + 1] : null;
console.log(`current:${x} next:${next} previous: ${prev}`)
})
If you don't want to use forEach you can use for..in. But besure about this you are not adding any properties other than indexes on the array.
const arr = ['apple', 'banana', 'grapes', 'blueberries']
for(let i in arr){
console.log(`current:${arr[+i]} next:${arr[+i+1]} previous: ${arr[+i-1]}`)
}
You could use forEach. The third parameter of the callback function is the array. So, you could destructure it to get the [i-1] and [i+1] th items
const my_fruits = ['apple', 'banana', 'grapes', 'blueberries']
my_fruits.forEach((item, i, { [i-1]: prev, [i+1]: next }) => {
console.log(item, prev, next)
})
You can access the index of the current element.
var my_fruits = ['apple', 'banana', 'grapes', 'blueberries']
for (const [index,value] of my_fruits.entries()) {
if(index) console.log(my_fruits[index-1])
}
You can use a regular for (let i = 0; i < len; i++)
let my_fruits = ['apple', 'banana', 'grapes', 'blueberries'];
for (let i = 0, len = my_fruits.length; i < len; i++)
{
if (i == 0)
{
console.log("I am at first iteration. The last item is : "+ my_fruits[len - 1]);
}
if (i > 0)
{
console.log("I have now : " + my_fruits[i] + " and I have access to " + my_fruits[i - 1]);
}
if (i + 1 == len)
{
console.log("I am at last iteration. The first item is : "+ my_fruits[i - 1]);
}
}
I hope that what You need
const my_fruits = ['apple', 'banana', 'grapes', 'blueberries']
for (let fruit in my_fruits) {
fruit = parseInt(fruit);
if (! my_fruits[fruit + -1]) {
console.log(my_fruits[0] + " => " + my_fruits[fruit + my_fruits.length - 1]);
} else {
console.log(my_fruits[fruit] + " => " + my_fruits[fruit + -1]);
}
}
const arr = ['apple', 'banana', 'grapes', 'blueberries']
arr.forEach((x,i) => {
let prev = i > 0 ? arr[i - 1] : null;
let next = i + 1 < arr.length ? arr[i + 1] : null;
console.log(`current:${x} next:${next} previous: ${prev}`)
})
I used Maheer Ali's code above, but I had to make a change.
I put i + 1 < arr.length instead of i < arr.length because otherwise it always returns true (the index, starting from 0, will always be less than the length of the array, if it exists).

How to convert array to array of objects?

I have
let array = ['mango', 'mango_shake','banana', 'banana_shake', 'cherry', 'cherry_shake', 'Strawberry', 'Strawberry_shake', ...n];
What i want to do is:
let target = [{'fruit': 'mango', 'drink': 'mango_shake'},
{'fruit': 'banana', 'drink': 'banana_shake'}, ...n];
How can i do it?
You can simply loop through array and create an array of object like this
let array = ['mango', 'mango_shake', 'banana', 'banana_shake', 'cherry', 'cherry_shake', 'Strawberry', 'Strawberry_shake'];
var res = [];
for (var i = 0; i < array.length; i = i + 2) {
var ob = {};
ob.fruit = array[i];
ob.drink = array[i + 1];
res.push(ob);
}
console.log(res);
Note: This answer assumes the fruit and its corresponding drink are always right beside each other in the array. This will give wrong answers if items are out of order.
Just iterate over your original array until it is empty and take out pairs and map them to objects:
const result = [];
while(array.length)
result.push((([fruit, drink]) => ({fruit, drink}))(array.splice(0, 2));
(In case this is your homework: i think it will be harder to explain to your teacher how it works instead of just trying it on your own :))
You can iterate over the array to combine every other item
let target = {};
array.forEach( (curr, indx, arr) => {
if (indx %2 == 1) {
target[arr[indx-1]] = curr
}
});

Dedup array and sort array by most frequent occurrence (with LoDash)

Say I have an array that goes something like:
fruit_basket = ['apple', 'orange', 'banana', 'pear', 'banana']
I want to make an array fruits that consists of the fruits found in fruit basket, sorted in order of most frequently occurring fruit. (If there are ties I don't care about ordering.)
So one valid value for fruits is:
['banana', 'orange', 'apple', 'pear']
What's the most concise way to achieve this using LoDash? I don't care about run time performance.
First you'd count the occurences
var x = _.chain(fruit_basket).countBy(); // {apple: 1, orange: 1, banana: 2, pear: 1}
Then you'd pair them and sort by the number of occurences, using reverse to get the largest number first
var y = x.toPairs().sortBy(1).reverse(); //[["banana",2],["pear",1],["orange",1],["apple",1]]
Then you'd just map back the keys, and get the value as an array
var arr = y.map(0).value(); // ['banana', 'orange', 'apple', 'pear']
All chained together, it looks like
var arr = _.chain(fruit_basket).countBy().toPairs().sortBy(1).reverse().map(0).value();
Without loDash, something like this would do it
var fruit_basket = ['apple', 'orange', 'banana', 'pear', 'banana'];
var o = {};
fruit_basket.forEach(function(item) {
item in o ? o[item] += 1 : o[item] = 1;
});
var arr = Object.keys(o).sort(function(a, b) {
return o[a] < o[b];
});
Here's my take on this.
It doesn't use lodash as the question asks for. It's a vanilla alternative. I feel this could be valuable to people who land here from Google and don't want to use lodash (that's how I got here, at least).
const orderByCountAndDedupe = arr => {
const counts = new Map();
arr.forEach( item => {
if ( !counts.has(item) ) {
counts.set(item, 1);
} else {
counts.set(item, counts.get(item)+1);
}
});
return (
Array.from(counts)
.sort( (a, b) => b[1] - a[1])
.map( ([originalItem, count]) => originalItem)
);
};
An approach using for loop Array.prototype.splice()
var fruit_basket = ['apple', 'orange', 'banana', 'pear', 'banana'];
var res = [];
for (var i = 0; i < fruit_basket.length; i++) {
var index = res.indexOf(fruit_basket[i]);
// if current item does not exist in `res`
// push current item to `res`
if (index == -1) {
res.push(fruit_basket[i])
} else {
// else remove current item , set current item at index `0` of `res`
res.splice(index, 1);
res.splice(0, 0, fruit_basket[i])
}
}
console.log(res)
You can count occurences using _.countBy and then use it in _.sortBy:
var counter = _.countBy(fruit_basket)
var result = _(fruit_basket).uniq().sortBy(fruit => counter[fruit]).reverse().value()

Sort Array by occurrence of its elements

I'm looking for an elegant way of sorting an array by the occurrence of its elements.
For example, in:
['pear', 'apple', 'orange', 'apple', 'orange', 'apple']
the output should look like
['apple', 'orange', 'pear']
I have tried to loop through the array and save the occurrence in another temporary array, but this solution was quite bad.
It would require two loops.
var arr = ['pear', 'apple', 'orange', 'apple', 'orange', 'apple'];
//find the counts using reduce
var cnts = arr.reduce( function (obj, val) {
obj[val] = (obj[val] || 0) + 1;
return obj;
}, {} );
//Use the keys of the object to get all the values of the array
//and sort those keys by their counts
var sorted = Object.keys(cnts).sort( function(a,b) {
return cnts[b] - cnts[a];
});
console.log(sorted);
Map the values into a fruit→count associated object:
var counted = fruits.reduce(function (acc, fruit) {
if (acc[fruit]) {
acc[fruit]++;
} else {
acc[fruit] = 1;
}
return acc;
}, {});
Map that object into a sortable array:
var assoc = counted.keys().map(function (fruit) {
return [fruit, counted[fruit]];
});
Sort the array:
assoc.sort(function (a, b) { return a[1] - b[1]; });
Extract the values:
var result = assoc.map(function (i) { return i[0]; });
You can reduce your array to remove duplicates and then sort with custom comparator:
var sorted = ['pear', 'apple', 'orange', 'apple', 'orange', 'apple'].reduce(function(result, item) {
result.every(function(i) {
return i != item;
}) && result.push(item);
return result;
}, []).sort(function(i1, i2) {
return i1 > i2;
});
console.log(sorted);
With linq.js it is pretty easy (with example):
var array = ['pear', 'apple', 'orange', 'apple', 'orange', 'apple'];
var res = Enumerable.From(array).GroupBy(function (x) { return x; }).Select(function (x) { return { key: x.Key(), count: x.Count() } }).OrderByDescending(function (x) { return x.count }).Select(function (x) { return x.key}).ToArray();
You could try out lodash. All you need to do is group, sort, then map and you are done.
var arr = ['pear', 'apple', 'orange', 'apple', 'orange', 'apple'];
document.body.innerHTML = _(arr).chain().groupBy()
.sortBy(function(a) {
return -a.length; // <- Note: Remove the negation to sort in ascending order.
}).map(function(a) {
return a[0];
}).value().join(', ');
<script src="https://cdn.rawgit.com/lodash/lodash/master/dist/lodash.js"></script>
Try this:
var arr = ['pear', 'apple', 'orange', 'apple', 'orange', 'apple'];
var result = [];
var count = 0;
arr.sort();
for (var i = 0; i < arr.length; i++) {
count++;
if (arr[i] != arr[i + 1]) {
result.push({
"count": count,
"value": arr[i]
});
count = 0;
}
}
result.sort(function(a, b) {
if (a.count < b.count) return 1;
if (a.count > b.count) return -1;
return 0;
});
console.log(result.map(function(item){return item.value}));

Categories