How to compare 2 arrays and get count of each matching item - javascript

Say an AJAX call returns 2 arrays on success. The arrays returned are different everytime, but there could be some elements in common. For example,
array1 = [a, b, c, d]
array2 = [a, b, b, b, a, c, b, c, c]
Now I want to get the number of times each element of array 1 appeared in array 2, in this case, the results would be:
a: 2
b: 4
c: 3
d: 0
I have the following code that compares the two arrays, but I can't figure out how to keep a counter, do I have to construct a new 2d array for each element of array1? In which case, how do I construct the array without knowing the elements inside of it first?
success: function (array1, array2) {
for (var i = 0; i < array1.length; i++) {
for (var j = 0; j < array2.length; j++) {
if (array2[j] == array1[i]) {
console.log("found match: " + array2[j]);
// counting
// count should go up by 1
}
}
}
}

var array1 = ['a', 'b', 'c', 'd'];
var array2 = ['a', 'b', 'b', 'b', 'a', 'c', 'b', 'c', 'c'];
var result = {}
array1.forEach(function(item) {
result[item] = 0
})
array2.forEach(function(item) {
if(result.hasOwnProperty(item)) {
result[item]++
}
})
console.log(result);

To achieve this you just need to loop through array1 to get the unique values, then you can use filter() to find how many of them exist in the second array. Try this:
var array1 = ['a', 'b', 'c', 'd'];
var array2 = ['a', 'b', 'b', 'b', 'a', 'c', 'b', 'c', 'c'];
var result = {};
array1.forEach(function(item) {
result[item] = array2.filter(t => t == item).length;
})
console.log(result);

You may do as follows in O(n);
var arr1 = ["a", "b", "c", "d"],
arr2 = ["a", "b", "b", "b", "a", "c", "b", "c", "c"],
result = arr2.reduce((h,p) => (h.hasOwnProperty(p) && h[p]++, h) ,arr1.reduce((m,k) => (m[k] = 0,m),{}));
console.log(result);

Related

How do you merge certain items in a JavaScript array?

Say you have the following array:
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
How would you change this array so that all the "b" items get grouped together, until you hit another "a".
So the result of the above array would look like:
['a', 'a', 'bbb', 'a', 'bb', 'a'];
I'm trying to solve a problem with wrapping span tags around words that match a patter in a React app, but this is essentially my problem.
I've been working at it for ages but can't come up with anything I'm happy with.
Any ideas?
Cheers.
Count repeating occurences, then build the result based on that:
const result = [];
let curr = array[0], count = 1;
for(const el of array.slice(1).concat(undefined)) {
if(el !== curr || el !== "b") {
result.push(curr.repeat(count));
curr = el, count = 1;
} else count++;
}
Assuming the elements will always be single letters, you can merge the elements, then match on either bs or non-bs:
ab.join('').match(/(b+|.)/g)
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
let output = ab.join('').match(/(b+|.)/g);
console.log(output);
Using Array#reduce you could do something like this.
I'm assuming the first two characters in your solution were a typo.
const data = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
const res = data
.reduce((a,c)=>{
const lastIndex = a.length - 1;
if(a[lastIndex] && a[lastIndex].includes('b') && c === 'b') a[lastIndex] += c;
else a.push(c);
return a;
}, []);
console.log(res);
I don't know how to give an explanation for this but using reduce you can do it like this:
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
function merge(arr) {
return arr.reduce((acc,cur, index) => {
if(arr[index - 1] === 'b' && cur !== 'a') {
acc[acc.length - 1] = acc[acc.length - 1] + cur;
return acc;
}
acc.push(cur);
return acc;
},[]);
}
console.log(merge(ab))
Here's what you're after:
var ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
var index = 0;
var groupedArray = [];
var firstOccurence = true;
var groupedString = "";
var grouping = false;
for (var a = 0; a < ab.length; a++) {
if (ab[a] == "a") {
if (grouping) {
grouping = false;
firstOccurence = true;
groupedArray.push(groupedString);
}
groupedArray.push(ab[a]);
} else {
if (firstOccurence) {
groupedString = "";
firstOccurence = false;
}
groupedString += ab[a];
grouping = true;
}
}
console.log(groupedArray);
If you just want to merge b then you could use reduce like this:
If the current item and the previous item are b, then append it to the last accumulator item. Else, push it to the accumulator
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a']
const output = ab.reduce((acc, c, i, arr) => {
arr[i-1] === "b" && c === "b"
? acc[acc.length - 1] += c
: acc.push(c)
return acc;
},[])
console.log(output)
You can just map through the ab array and if the current element is a, push it to a new array but if the current element is b, check if the previous element is b or not, and if it is, merge the current element to the previous element else just push the b to the new array.
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
let arr = [];
ab.map((e,i) => {
if(i > 0) { // check if element is the first one or not
if(e == "b") {
if(ab[i - 1].indexOf('b') > -1) { // check if prev element is "b" or not
arr[arr.length-1] += e; // merge if prev element is "b"
} else {
arr.push(e);
}
} else {
arr.push(e);
}
} else {
arr.push(e);
}
});
console.log(arr);
N.B. The reduce() method approach and the regex approach shown in the other answers are cleaner and more concise as compared to the map() method approach shown above though.
you can stringify your array then split it with a match of b or more or any other character
const ab = ['a', 'a', 'b', 'b', 'b', 'a', 'b', 'b', 'a'];
const res = ab.join("").match(/(b{1,}|.)/g);
console.log(res)

JavaScript Join Identical Adjacent Elements in an Array

Input:
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'e', 'e']
Desired output:
['aaa', 'bb', 'ccc', 'd', 'ee']
Is this possible?
Edit: I forgot to mention that my previous attempt (for another example) failed, and I cannot figure out why:
let newArr = []
let last
let current
for (var i = 0; i < arr.length; i ++) {
last = last || isCurrencyArr[i]
current = isCurrencyArr[i]
let str = ''
if (last === current) {
str += arr[i]
} else {
newArr.push(str)
str = ''
}
last = isCurrencyArr[i]
}
Your example has a few hiccups. It redeclares str inside each iteration, therefore it only ever pushes empty strings. Also, it pushes the previous string when it comes across a new item, but it doesn't account for scenarios where the last items are the same, as in your example with the letter e.
If you're joining alike elements together, regardless of position...
Instead, you could use reduce() and spread syntax for object literals to build an object that keeps track of the occurrences of each item.
The object after reduce() looks like this:
{ a: "aaa", b: "bb", c: "ccc", d: "d", e: "ee" }
Once that object is built, all we have to do is create an array from the values using Object.values().
const arr = ['a', 'b', 'a', 'c', 'b', 'a', 'e', 'd', 'c', 'e', 'c'];
let items = arr.reduce((acc,i) => acc[i] ? {...acc, [i]: acc[i]+i } : {...acc, [i]: i }, {});
let result = Object.values(items);
console.log(result);
If you only want to join adjacent alike elements...
The example below uses a slightly similar approach to the above, however this reduce() outputs a string. The logic is similar to your own example: if the previous item is the same, add it to a string. If it is not, separate it and keep going.
The result is something like this: aaa|bb|ccc|d|ee. To turn that into an array, we just need to do split("|").
const arr = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'e', 'e'];
let result = arr
.reduce((acc,i,idx,a) => (a[idx-1] === i || idx===0) ? acc+i : acc+"|"+i, "")
.split("|");
console.log(result);
This can be a solution to join adjacent elements:
const arr = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'e', 'e'];
const resp = arr.reduce((a, e) => {
if(a.length === 0) return a.concat(e);
if(e === a[a.length - 1].split('').reverse()[0]) {
a[a.length - 1] = a[a.length - 1].split('').concat(e).join('');
return a;
}
return a.concat(e);
}, [])
console.log(resp);
Something like this should work:
function simplify(arr) {
let current = arr[0];
let final_arr = [];
let accumulated = current;
for (let i = 1; i < arr.length; i += 1) {
if (current === arr[i]) {
accumulated += arr[i];
} else {
final_arr.push(accumulated)
current = arr[i];
accumulated = current;
}
}
final_arr.push(accumulated);
return final_arr;
}
Using Array#reduce, spread syntax, and Map.
const data = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'e', 'e'];
const res = [...data.reduce((a,c)=>{
return a.set(c, (a.get(c)||"") + c);
}, new Map()).values()];
console.log(res);
Algo for strictly adjacent elements.
const data = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'e', 'e'];
const res = [];
for(let i = 0; i < data.length; i++){
const c = data[i];
let str = c;
for(let j = i + 1; j < data.length && c === data[j]; j++,i++){
str += c;
}
res.push(str);
}
console.log(res);

How do I return the similar values between 2 arrays, and in the order of the second?

I have 2 arrays. I am trying to return the similar values between the 2 but in the order of the second. For example, take a look at the two arrays:
array1 = ['a', 'b', 'c']
array2 = ['b', 'c', 'a', 'd']
What I would like to return is this:
sim = ['b', 'c', 'a']
Here is a link to what I am trying to accomplish. Currently the script is faulty and not catching the corner case.
You could use a Set for array1 use Array#filter array2 by checking the set.
var array1 = ['a', 'b', 'c'],
array2 = ['b', 'c', 'a', 'd'],
theSet = new Set(array1),
result = array2.filter(v => theSet.has(v));
console.log(result);
Some annotations to your code:
function arr_sim (a1, a2) {
var //a = {}, // take an object as hash table, better
a = Object.create(null), // a really empty object without prototypes
sim = [],
i; // use single declaration at top
for (i = 0; i < a1.length; i++) { // iterate all item of array 1
a[a1[i]] = true;
}
for (var i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
sim.push(a2[i]); // just push the value
}
}
return sim;
}
console.log(arr_sim(['a', 'b', 'c'], ['b', 'c', 'a', 'd']));
You can iterate array2 with a filter, and check if the value is contained in array1:
let array1 = ['a', 'b', 'c'];
let array2 = ['b', 'c', 'a', 'd'];
let sim = array2.filter((entry) => {
return array1.includes(entry);
});
console.log(sim);
I think this is what you are looking for?
function arr_sim (a1, a2) {
a1 = Array.isArray(a1)?a1:typeof a1 == "string"?a1.split(""):false;
a2 = Array.isArray(a2)?a1:typeof a2 == "string"?a2.split(""):false;
if(!a1 || !a2){
alert("Not valid values");
return;
}
var filterArray = a1.filter(function(val){
return a2.indexOf(val) !== -1;
})
return filterArray;
}
console.log(arr_sim(['a', 'b'], ['b', 'a', 'c', 'd']));
console.log(arr_sim("abcd", "abcde"));
console.log(arr_sim("cxz", "zcx"));
Try this
const arr_sim = (a1, a2) => a2.filter(a => a1.includes(a))
console.log(arr_sim(['a', 'b', 'c'], ['b', 'c', 'a', 'd']));
try this example here similar-values betwe
en two arrays
var a1 = ['a' ,'b'];
var a2 = ['a' ,'b' ,'c'];
var result = arr_sim(a1,a2);// call method arr_sim
console.log(result);
function arr_sim (a1, a2) {
var similar = [];
for( var i = 0 ; i <a1.length ; i++ ){ // loop a1 array
for( var j = 0 ; j <a2.length ; j++ ){ // loop a2 array
if( a1[i] == a2[j] ){ // check if is similar
similar.push(a1[i]); // add to similar array
break; // break second loop find that is similar
} // end if
} // end second lopp
} // end first loop
return similar; // return result
} // end function

How to create a Set from Array and remove original items in JavaScript

I have an Array with duplicate values.
I want to create a Set to get the distinct values of that array and remove or create a new Array that will have the same data MINUS the elements required to create the Set.
This is not just a matter of remove the duplicates, but remove a SINGLE entry of a each distinct value in the original array
Something like that works, but I wonder if there is a more direct approach:
let originalValues = [
'a',
'a',
'a',
'b',
'b',
'c',
'c',
'd'
];
let distinct = new Set(originalValues);
/*
distinct -> { 'a', 'b', 'c', 'd' }
*/
// Perhaps originalValues.extract(distinct) ??
for (let val of distinct.values()) {
const index = originalValues.indexOf(val);
originalValues.splice(index, 1);
}
/*
originalValues -> [
'a',
'a',
'b',
'c'
];
*/
Use Array#filter in combination with the Set:
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'];
const remainingValues = originalValues.filter(function(val) {
if (this.has(val)) { // if the Set has the value
this.delete(val); // remove it from the Set
return false; // filter it out
}
return true;
}, new Set(originalValues));
console.log(remainingValues);
You could use closure over a Set and check for existence.
let originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'],
result = originalValues.filter((s => a => s.has(a) || !s.add(a))(new Set));
console.log(result);
You should not use indexOf inside a loop, because it has linear cost, and the total cost becomes quadratic. What I would do is use a map to count the occurrences of each item in your array, and then convert back to an array subtracting one occurrence.
let originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'];
let freq = new Map(); // frequency table
for (let item of originalValues)
if (freq.has(item)) freq.set(item, freq.get(item)+1);
else freq.set(item, 1);
var arr = [];
for (let [item,count] of freq)
for (let i=1; i<count; ++i)
arr.push(item);
console.log(arr);
If all items are strings you can use a plain object instead of a map.
You can create a simple Array.prototype.reduce loop with a hash table to count the number of occurrences and populate the result only if it occurs more than once.
See demo below:
var originalValues=['a','a','a','a','b','b','b','c','c','d'];
var result = originalValues.reduce(function(hash) {
return function(p,c) {
hash[c] = (hash[c] || 0) + 1;
if(hash[c] > 1)
p.push(c);
return p;
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
Instead of using Set for this you could just use reduce() and create new array with unique values and also update original array with splice().
let oV = ["a", "a", "a", "a", "b", "b", "c", "c", "d"]
var o = {}
var distinct = oV.reduce(function(r, e) {
if (!o[e]) o[e] = 1 && r.push(e) && oV.splice(oV.indexOf(e), 1)
return r;
}, [])
console.log(distinct)
console.log(oV)
As an alternate approach, you can use following algorithm that will remove only 1st entry of a duplicate element. If not duplicate, it will not remove anything.
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'];
var r = originalValues.reduce(function(p, c, i, a) {
var lIndex = a.lastIndexOf(c);
var index = a.indexOf(c)
if (lIndex === index || index !== i)
p.push(c);
return p
}, [])
console.log(r)
If duplicates are not case, then you can directly remove first iteration directly
const originalValues = ['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd'];
var r = originalValues.filter(function(el, i) {
return originalValues.indexOf(el) !== i
})
console.log(r)

JavaScript Array mapping

Consider the following scenario;
var defaultArr = ['a', 'b', 'c', 'd'];
var availArr = [];
var selectedArr = [];
If I am passing array some index's value in param's, I need to split up my array's
Example:
If Array Index : 0,2
Expected result:
availArr = ['b', 'd'];
selectedArr = ['a', 'c'];
Is there any default method to achieve this?
Failrly easy with Array.reduce
var defaultArr = ['a', 'b', 'c', 'd'];
var indexes = [0,2];
var result = defaultArr.reduce(function(p, c, i){
if(indexes.indexOf(i)>-1)
p.selectedArr.push(c);
else
p.availArr.push(c);
return p;
}, {availArr: [], selectedArr:[]});;
console.log('availArr',result.availArr);
console.log('selectedArr',result.selectedArr);
This works because reduce takes a callback argument which is passed 3 arguments - in my example above
p the seed object passed in
c the current array element
i the index of the current element
And uses that information along with indexOf to determine which result array to push to.
You could use Array#reduceRight and iterate the indices array.
var defaultArr = ['a', 'b', 'c', 'd'],
availArr = defaultArr.slice(),
selectedArr = [],
indices = [0, 2];
indices.reduceRight(function (_, a) {
selectedArr.unshift(availArr.splice(a, 1)[0]);
}, 0);
console.log(availArr);
console.log(selectedArr);
var defaultArr = ['a', 'b', 'c', 'd'];
var availArr = [];
var selectedArr = [];
function splitArray(indexes) {
availArr = defaultArr;
indexes.forEach(function(idx) {
let item = availArr.splice(idx, 1)[0];
selectedArr.push(item);
})
}
splitArray([0, 2]);
console.log(availArr);
console.log(selectedArr);
You can use Array methods like forEach and includes
var given = ['a', 'b', 'c', 'd'];
var indexes = [0, 2];
var available = [];
var selected = [];
given.forEach(function (v, i) {
if (indexes.includes(i)) {
selected.push(v);
} else {
available.push(v);
}
});
document.write(JSON.stringify({
given: given,
available: available,
selected: selected
}));
In JS Array.prototype.reduceRight() is the ideal functor to iterate over an array and to morph it by removing items. Accordingly i would approach this job as follows;
var defaultArr = ['a', 'b', 'c', 'd'],
indices = [0, 2];
result = defaultArr.reduceRight((p,c,i,a) => indices.includes(i) ? p.concat(a.splice(i,1)) : p ,[]);
console.log(defaultArr,result);
You can use array.splice + array.concat to achieve this
var defaultArr = ['a', 'b', 'c', 'd'];
var availArr = [];
var selectedArr = [];
function parseIndexes(indexArr){
var deleteCount = 0;
availArr = defaultArr.map(x=>x);
indexArr.forEach(function(i){
selectedArr = selectedArr.concat(availArr.splice(i-deleteCount,1))
deleteCount++;
});
console.log(availArr, selectedArr)
}
parseIndexes([0,2])
With only Array.filter
var array = ['a', 'b', 'c', 'd'];
var indexes = [0, 2]
array.filter(function(el, i) {
return indexes.indexOf(i) !== -1
});
// ["a", "c"]
With array the array of your elements, objects, strings... and indexes the array containing all the indexes of the elements you want to keep, you just remove from the arrayarray all the elements whose id isn't in theindexes array.
The array of all selected entries can be obtained in one line via the Array.map:
var defaultArr = ['a', 'b', 'c', 'd']
var index = [0,2]
var selectedArr = index.map(i => defaultArr[i]) //=> ['a', 'c']
Then the array of the remaining entries can be retrieved e.g. with the Ramda's difference operator:
var availArr = R.difference(defaultArr, selectedArr) //=> ['b', 'd']

Categories

Resources