I have a json array of the form
[{'from':'a','to':'b','type':'add','value':'100','op':'cr'},
{'from':'a','to':'b','type':'add','value':'200','op':'dr'},
{'from':'a','to':'b','type':'add','value':'300','op':'cr'},
{'from':'c','to':'d','type':'sub','value':'400','op':'dr'},
{'from':'c','to':'d','type':'sub','value':'500','op':'cr'}]
I want the output as
[{'from':'a','to':'b','add':[{'100':'cr'},{'200':'dr'},{'300':'cr'}]},
{'from':'c','to':'d','sub':[{'400':'dr'},{'500':'cr'}]}]
How to do it in Javascript/NodeJS?
You could use an object as hash table and assign the values via the key with parts from from and to.
var data = [{ from: 'a', to: 'b', option: '100' }, { from: 'a', to: 'b', option: '200' }, { from: 'a', to: 'b', option: '300' }, { from: 'c', to: 'd', option: '400' }, { from: 'c', to: 'd', option: '500' }],
grouped = [];
data.forEach(function (a) {
var key = [a.from, a.to].join('|');
if (!this[key]) {
this[key] = { from: a.from, to: a.to, option: [] };
grouped.push(this[key]);
}
this[key].option.push(a.option);
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try the following code snippet -
'use strict';
var x = [{ 'from': 'a', 'to': 'b', 'option': '100' },
{ 'from': 'a', 'to': 'b', 'option': '200' },
{ 'from': 'a', 'to': 'b', 'option': '300' },
{ 'from': 'c', 'to': 'd', 'option': '400' },
{ 'from': 'c', 'to': 'd', 'option': '500' }
];
var match = false;
x.reduce(function(returnVal, item) {
match = false;
returnVal.map(function(each) {
if (each.from === item.from && each.to === item.to) {
if (Array.isArray(each.option)) {
each.option.push(item.option);
} else {
each.option = [each.option];
each.option.push(item.option);
}
match = true;
}
return each;
})
if (!match) {
returnVal.push(item);
}
return returnVal;
}, []);
Using a simple loop to iterate through keys in a 'tmp' object which combined from 'from' and 'to' may help:
var input = [{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{ 'from': 'c', 'to': 'd', 'option': '500' }];
var tmp = {};
$.each(input, function (idx, obj) {
var key = obj.from + obj.to
tmp[key] = tmp[key] || { from: obj.from, to: obj.to};
tmp[key].option = tmp[key].option || [];
tmp[key].option.push(obj.option);
});
var output = [];
for(var key in tmp)
{
output.push(tmp[key]);
}
Wihtout jQuery, only javascript and cross browswer:
var array = [
{
'from': 'a',
'to': 'b',
'option': '100'
},
{
'from': 'a',
'to': 'b',
'option': '200'
},
{
'from': 'a',
'to': 'b',
'option': '300'
},
{
'from': 'c',
'to': 'd',
'option': '400'
},
{
'from': 'c',
'to': 'd',
'option': '500'
}
];
var array2 = [];
for (var a in array) {
for (var b in array2) {
if (array2[b].from == array[a].from && array2[b].to == array[a].to) {
array2[b].option.push(array[a].option);
break;
}
}
if (!array2[b] || array2[b].option.indexOf(array[a].option) == -1) {
array2.push({
from: array[a].from,
to: array[a].to,
option: [array[a].option]
});
}
}
console.log(array2);
You can use the below function unique to get the unique array out of the given array.
var array1 = [
{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}
];
function unique(array) {
var i = 0,
map = {}, // This map object stores the objects of array1 uniquely
uniqueArray = [],
obj,
len = array.length;
for (i = 0; i < len; i++) {
obj = array[i];
from = obj.from; to = obj.to;
// Create an id using from and to of the object
id = from + '-' + to;
// Check if object is already there in map object
if (map[id]) {
// If options is not an array then store the options in array
map[id].option = map[id].option instanceof Array ? map[id].option : [map[id].option];
map[id].option.push(obj.option);
}
// If object not available in map then create an object
else {
map[id] = {};
map[id].from = obj.from;
map[id].to = obj.to;
map[id].option = obj.option;
// Pushing the map object to the unique array
uniqueArray.push(map[id]);
}
}
return uniqueArray;
}
var array1 = [
{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}
];
var array2 = [];
for(var i=0; i<array1.length; i++) {
var obj = null,
from = array1[i]['from'],
to = array1[i]['to'];
for(var j=0; j<array2.length; j++) {
if (array2[j]['from'] == from && array2[j]['to'] == to) {
obj = array2[j];
break;
}
}
if (obj == null) {
obj = {'from':from,'to':to,'option':[]};
array2.push(obj);
}
obj['option'].push(array1[i]['option']);
}
console.log(array2);
JS Fiddle: https://jsfiddle.net/6wqkhms3/1/
var data = [{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}];
var out = [];
// Utility function which finds-out if this object is available in the RESULT array or not
function findObj(list, item) {
var resultObj;
for (var i in list) {
if (list[i].from === item.from && list[i].to === item.to) {
resultObj = list[i];
break;
}
}
return resultObj;
}
// EXECUTION
for (var i in data) {
// Check if this objec is available in the RESULT array,
if (findObj(out, data[i])) {
// If yes, then push the value to it
findObj(out, data[i]).option.push(data[i].option);
} else {
// If NO, then add this item to the RESULT array
out.push({
from: data[i].from,
to: data[i].to,
option: [data[i].option]
});
}
}
console.log(out);
Related
I have the following data array:
const data = [
{
value: [
'a',
'b',
'a',
'a'
]
},
{
value: [
'c',
'c',
'd',
'c'
]
}
];
So there's is 4 combination here based on index:
combination 1 : a - c (index 0 in each value arrays)
combination 2 : b - c (index 1 in each value arrays)
combination 3 : a - d (index 2 in each value arrays)
combination 4 : a - c (index 3 in each value arrays)
As you can see the first and the last combinations are the same, so i want to remove the second occurrence from each array, the result should be:
[
{
value: [
'a',
'b',
'a'
]
},
{
value: [
'c',
'c',
'd'
]
}
]
You can zip the values arrays from both objects to form an array which looks like:
["a-c", "b-c", ...]
As these are now strings, you can turn this array into a Set using new Set(), which will remove all duplicate occurrences. You can then turn this set back into an array which you can then use .reduce() on to build you array of objects from. For each value you can obtain the list of values by using .split() on the '-', and from that, populate your reduced array.
See example below:
const data = [{ value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c' ] } ];
const unq = [...new Set(
data[0].value.map((_,c)=> data.map(({value})=>value[c]).join('-'))
)];
const res = unq.reduce((acc, str) => {
const values = str.split('-');
values.forEach((value, i) => acc[i].value.push(value));
return acc;
}, Array.from({length: data.length}, _ => ({value: []})))
console.log(res);
Limitations of the above method assume that you won't have a - character as your string value. If this is an issue, you can consider using a different delimiter, or find unique values within your array using .filter() instead of a Set.
You could save a lookup object for unique pairs of value based with index
Given your input is, below solution could help you
const data = [
{
value: ["a", "b", "a", "a"],
},
{
value: ["c", "c", "d", "c"],
},
]
const lookup = {}
data[0].value.forEach((_, index) => {
lookup[`${data[0].value[index]}-${data[1].value[index]}`] = true
})
const res = Object.keys(lookup).reduce(
(acc, key) => {
const [val1, val2] = key.split("-")
acc[0].value.push(val1)
acc[1].value.push(val2)
return acc
},
[{ value: [] }, { value: [] }]
)
console.log(res)
Below is a two step solution with a generator function and a single pass.
const data = [ { value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c', ] } ];
const zipDataValues = function* (data) {
const iterators = data.map(item => item.value[Symbol.iterator]())
let iterations = iterators.map(iter => iter.next())
while (iterations.some(iteration => !iteration.done)) {
yield iterations.map(iteration => iteration.value)
iterations = iterators.map(iter => iter.next())
}
}
const filterOutDuplicateCombos = function (values) {
const combosSet = new Set(),
resultData = [{ value: [] }, { value: [] }]
for (const [valueA, valueB] of values) {
const setKey = [valueA, valueB].join('')
if (combosSet.has(setKey)) {
continue
}
combosSet.add(setKey)
resultData[0].value.push(valueA)
resultData[1].value.push(valueB)
}
return resultData
}
console.log(
filterOutDuplicateCombos(zipDataValues(data))
) // [ { value: [ 'a', 'b', 'a' ] }, { value: [ 'c', 'c', 'd' ] } ]
Here is a reference on generators and iterators
Filter combinations + sorting by the first occurrence:
const data = [{
value: ['a', 'b', 'a', 'a']
},{
value: ['c', 'c', 'd', 'c']
}];
var res = {}, i, t;
for (i = 0; i < data[0].value.length; ++i) {
res[data[0].value[i]] = res[data[0].value[i]] || {};
res[data[0].value[i]][data[1].value[i]] = true;
}
data[0].value = [];
data[1].value = [];
for (i in res) {
for (t in res[i]) {
data[0].value[data[0].value.length] = i;
data[1].value[data[1].value.length] = t;
}
}
console.log(data);
I need to create every combination of objects possible out of an array with types/identifiers. E.g.:
Input:
let inputArray =
[
{
'type': '1',
'values': [
'val1',
'val2'
]
},
{
'type': '2',
'values': [
'val1',
'val2',
'val3'
]
}
]
Output:
let outputArray =
[
[
{
'type': '1',
'value': 'val1'
},
{
'type': '2',
'value': 'val1'
}
],
[
{
'type': '1',
'value': 'val1'
},
{
'type': '2',
'value': 'val2'
}
],
[
{
'type': '1',
'value': 'val1'
},
{
'type': '2',
'value': 'val3'
}
],
[
{
'type': '1',
'value': 'val2'
},
{
'type': '2',
'value': 'val1'
}
],
[
{
'type': '1',
'value': 'val2'
},
{
'type': '2',
'value': 'val2'
}
],
[
{
'type': '1',
'value': 'val2'
},
{
'type': '2',
'value': 'val3'
}
],
]
And it needs to work dynamically depending on the number of types, and values inside the input array. I.E. the input array has another object, where that objects values array has 3 or 4 elements...
What I tried:
function doTheThing(inputArray) {
let outputArray = []
for (let element of inputArray) {
for (let value of element.values) {
outputArray.push({ type: element.type, value: value })
}
}
}
Which gives me every value possible, but doesn't give me every combination that I need...
You can create recursive function using simple for loop that exists when the value of n is larger then the length of the data or continues to increment it and then runs for loop.
let data = [{"type":"1","values":["val1","val2"]},{"type":"2","values":["val1","val2","val3"]}]
function f(data, n = 0, prev = []) {
const result = [];
if (n > data.length - 1) {
result.push(prev.slice())
return result;
}
const { type, values } = data[n];
for (let i = 0; i < values.length; i++) {
prev[n] = { type, value: values[i] }
result.push(...f(data, n + 1, prev))
}
return result;
}
const result = f(data);
console.log(JSON.stringify(result, 0, 4))
Or you could use the reduce method instead of the for loop and make recursive call as long as the n < data.length - 1
let data = [{"type":"1","values":["val1","val2"]},{"type":"2","values":["val1","val2","val3"]}]
function f(data, n = 0, prev = []) {
return data[n].values.reduce((r, value) => {
prev[n] = { type: data[n].type, value }
if (n === data.length - 1) {
r.push(prev.slice())
}
if (n < data.length - 1) {
r.push(...f(data, n + 1, prev))
}
return r;
}, [])
}
const result = f(data);
console.log(JSON.stringify(result, 0, 4))
This function can help us to extend a permutation:
let permute = (iterable, arr) => {
let vals = [ ...iterable ];
let result = [];
for (let v of arr)
for (let items of vals)
result.push([ v, ...items ]);
return result;
};
For example if we want to permute [ 'a', 'b' ], [ 1, 2 ], and [ '?', '!' ] we can do:
let permute = (iterable, arr) => {
let vals = [ ...iterable ];
let result = [];
for (let v of arr)
for (let items of vals)
result.push([ v, ...items ]);
return result;
};
let v1 = permute([ [] ], [ 1, 2 ]);
let v2 = permute(v1, [ 'a', 'b' ]);
let v3 = permute(v2, [ '?', '!' ]);
console.log(JSON.stringify({ v1, v2, v3 }));
You can use this function with inputArray after performing a basic transformation on it (to achieve the type of result you posted in your question). For an entry in inputArray like { type: '1', values: [ 'v1', 'v2' ] }, you need to convert that to a form like: [ { type: '1', value: 'v1' }, { type: '1', value: 'v2' } ].
// Your original data
let inputArray = [{'type': '1','values': ['val1','val2']},{'type': '2','values': ['val1','val2','val3']}];
// Function to format data as described
let format = arr => arr.map(({ type, values }) => values.map(value => ({ type, value })));
// The same `permute` function as before
let permute = (iterable, arr) => { let vals = [ ...iterable ]; let result = []; for (let v of arr) for (let items of vals) result.push([ v, ...items ]); return result; };
let formatted = format(inputArray);
let v1 = permute([ [] ], formatted[0]);
let v2 = permute(v1, formatted[1]);
console.log(JSON.stringify(v2, null, 2));
I have an object like this:
var database = [
{
category: 'CPUs',
id: 1,
products: [Product, Product, Product] //Product is an object
},
{
category: 'GPUs',
id: 2,
products: [Product, Product]
}
];
and so on..
I'd like to get 10 random products in total, non-repeating. There can be more than one from the same category, as long as they are different products. How can I do this? I tried this:
function getRandomFromObject(){
var productsCollected = [];
while(productsCollected.length < 10){
var randomCategory = database[Math.floor(Math.random()*database.length)];
var randomProduct = randomCategory.products[Math.floor(Math.random()*randomCategory.products.length)];
productsCollected.push(randomProduct);
}
return productsCollected;
}
Things become easier if you first concatenate all the products into one array, then shuffle that array and finally take the first 10 from that:
function shuffle(a) {
for (let i = a.length; i; i--) {
let j = Math.floor(Math.random() * i);
[a[i - 1], a[j]] = [a[j], a[i - 1]];
}
return a;
}
function getRandomFromObject(count){
return shuffle([].concat(...database.map(o => o.products))).slice(0, count);
}
var database = [
{
category: 'CPUs',
id: 1,
products: ['a', 'b', 'c'] //Product is an object
},
{
category: 'GPUs',
id: 2,
products: ['d', 'e']
},
{
category: 'GPUs',
id: 3,
products: ['f', 'g', 'h', 'i', 'j']
}
];
console.log(getRandomFromObject(10).join(','));
Addendum: If you can have the same Product object occurring in different categories, then apply a Set to the concatenated array, so to eliminate these duplicates:
return shuffle([...new Set([].concat(...database.map(o => o.products)))]).slice(0, count);
ES5 Code
As you asked in comments for ES5, and the need to consider products with the same ISBN property as the same products, here is the code for that:
function shuffle(a) {
for (var i = a.length; i; i--) {
var j = Math.floor(Math.random() * i);
var temp = a[i - 1];
a[i - 1] = a[j];
a[j] = temp;
}
return a;
}
function getRandomFromObject(count){
var uniq = {}; // Unique list of products, keyed by ISBN
database.forEach(function (o) {
o.products.forEach(function (product) {
uniq[product.isbn] = product;
});
});
var products = [];
for (product in uniq) {
products.push(uniq[product]);
}
return shuffle(products).slice(0, count);
}
var database = [
{
category: 'CPUs',
id: 1,
products: [{ isbn: 'a' }, { isbn: 'b' }, { isbn: 'c' }] //Product is an object
},
{
category: 'GPUs',
id: 2,
products: [{ isbn: 'd' }, { isbn: 'a' }, { isbn: 'j' }] // has same isbn as in CPUs
},
{
category: 'Others',
id: 3,
products: [{ isbn: 'e' }, { isbn: 'f' }, { isbn: 'g' }, { isbn: 'h' }, { isbn: 'i' }]
}
];
console.log(getRandomFromObject(10));
This question already has answers here:
How to get the difference between two arrays in JavaScript?
(84 answers)
Closed 5 months ago.
I am looking for an efficient way to remove all elements from a javascript array if they are present in another array.
// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// and this one:
var toRemove = ['b', 'c', 'g'];
I want to operate on myArray to leave it in this state: ['a', 'd', 'e', 'f']
With jQuery, I'm using grep() and inArray(), which works well:
myArray = $.grep(myArray, function(value) {
return $.inArray(value, toRemove) < 0;
});
Is there a pure javascript way to do this without looping and splicing?
Use the Array.filter() method:
myArray = myArray.filter( function( el ) {
return toRemove.indexOf( el ) < 0;
} );
Small improvement, as browser support for Array.includes() has increased:
myArray = myArray.filter( function( el ) {
return !toRemove.includes( el );
} );
Next adaptation using arrow functions:
myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );
ECMAScript 6 sets can permit faster computing of the elements of one array that aren't in the other:
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = new Set(['b', 'c', 'g']);
const difference = myArray.filter( x => !toRemove.has(x) );
console.log(difference); // ["a", "d", "e", "f"]
Since the lookup complexity for the V8 engine browsers use these days is O(1), the time complexity of the whole algorithm is O(n).
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
var toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))
The filter method should do the trick:
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];
// ES5 syntax
const filteredArray = myArray.filter(function(x) {
return toRemove.indexOf(x) < 0;
});
If your toRemove array is large, this sort of lookup pattern can be inefficient. It would be more performant to create a map so that lookups are O(1) rather than O(n).
const toRemoveMap = toRemove.reduce(
function(memo, item) {
memo[item] = memo[item] || true;
return memo;
},
{} // initialize an empty object
);
const filteredArray = myArray.filter(function (x) {
return toRemoveMap[x];
});
// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
...memo,
[item]: true
}), {});
const filteredArray = myArray.filter(x => toRemoveMap[x]);
If you are using an array of objects. Then the below code should do the magic, where an object property will be the criteria to remove duplicate items.
In the below example, duplicates have been removed comparing name of each item.
Try this example. http://jsfiddle.net/deepak7641/zLj133rh/
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
var toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
for( var i=myArray.length - 1; i>=0; i--){
for( var j=0; j<toRemove.length; j++){
if(myArray[i] && (myArray[i].name === toRemove[j].name)){
myArray.splice(i, 1);
}
}
}
alert(JSON.stringify(myArray));
Lodash has an utility function for this as well:
https://lodash.com/docs#difference
How about the simplest possible:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var toRemove = ['b', 'c', 'g'];
var myArray = myArray.filter((item) => !toRemove.includes(item));
console.log(myArray)
I just implemented as:
Array.prototype.exclude = function(list){
return this.filter(function(el){return list.indexOf(el)<0;})
}
Use as:
myArray.exclude(toRemove);
You can use _.differenceBy from lodash
const myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
const toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
const sorted = _.differenceBy(myArray, toRemove, 'name');
Example code here: CodePen
If you cannot use new ES5 stuff such filter I think you're stuck with two loops:
for( var i =myArray.length - 1; i>=0; i--){
for( var j=0; j<toRemove.length; j++){
if(myArray[i] === toRemove[j]){
myArray.splice(i, 1);
}
}
}
Now in one-liner flavor:
console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))
Might not work on old browsers.
This is pretty late but adding this to explain what #mojtaba roohi has answered. The first block of code will not work as each array is having a different object, i.e. df[0] != nfl[2]. Both objects look similar but are altogether different, which is not the case when we use primitive types like numbers.
let df = [ {'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }]
let res = nfl.filter(x => df.indexOf(x)<0)
console.log(res)
Here is the working code:
let df = [{'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }];
let res = nfl.filter((o1) => !df.some((o2) => o1.name === o2.name));
console.log(res)
If you're using Typescript and want to match on a single property value, this should work based on Craciun Ciprian's answer above.
You could also make this more generic by allowing non-object matching and / or multi-property value matching.
/**
*
* #param arr1 The initial array
* #param arr2 The array to remove
* #param propertyName the key of the object to match on
*/
function differenceByPropVal<T>(arr1: T[], arr2: T[], propertyName: string): T[] {
return arr1.filter(
(a: T): boolean =>
!arr2.find((b: T): boolean => b[propertyName] === a[propertyName])
);
}
Proper way to remove all elements contained in another array is to make source array same object by remove only elements:
Array.prototype.removeContained = function(array) {
var i, results;
i = this.length;
results = [];
while (i--) {
if (array.indexOf(this[i]) !== -1) {
results.push(this.splice(i, 1));
}
}
return results;
};
Or CoffeeScript equivalent:
Array.prototype.removeContained = (array) ->
i = #length
#splice i, 1 while i-- when array.indexOf(#[i]) isnt -1
Testing inside chrome dev tools:
19:33:04.447 a=1
19:33:06.354 b=2
19:33:07.615 c=3
19:33:09.981 arr = [a,b,c]
19:33:16.460 arr1 = arr
19:33:20.317 arr1 === arr
19:33:20.331 true
19:33:43.592 arr.removeContained([a,c])
19:33:52.433 arr === arr1
19:33:52.438 true
Using Angular framework is the best way to keep pointer to source object when you update collections without large amount of watchers and reloads.
I build the logic without using any built-in methods, please let me know any optimization or modifications.
I tested in JS editor it is working fine.
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chirag', place: 'bangalore'},
{name: 'chandan', place: 'mumbai'},
];
var toRemove = [
{name: 'chirag', place: 'bangalore'},
{name: 'deepak', place: 'bangalore'},
/*{name: 'chandan', place: 'mumbai'},*/
/*{name: 'alok', place: 'berhampur'},*/
];
var tempArr = [];
for( var i=0 ; i < myArray.length; i++){
for( var j=0; j<toRemove.length; j++){
var toRemoveObj = toRemove[j];
if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
break;
}else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
var fnd = isExists(tempArr,myArray[i]);
if(!fnd){
var idx = getIdex(toRemove,myArray[i])
if (idx === -1){
tempArr.push(myArray[i]);
}
}
}
}
}
function isExists(source,item){
var isFound = false;
for( var i=0 ; i < source.length; i++){
var obj = source[i];
if(item && obj && obj.name === item.name){
isFound = true;
break;
}
}
return isFound;
}
function getIdex(toRemove,item){
var idex = -1;
for( var i=0 ; i < toRemove.length; i++){
var rObj =toRemove[i];
if(rObj && item && rObj.name === item.name){
idex=i;
break;
}
}
return idex;
}
//Using the new ES6 Syntax
console.log(["a", "b", "c", "d", "e", "f", "g"].filter(el => !["b", "c", "g"].includes(el)));
// OR
// Main array
let myArray = ["a", "b", "c", "d", "e", "f", "g"];
// Array to remove
const toRemove = ["b", "c", "g"];
const diff = () => (myArray = myArray.filter((el) => !toRemove.includes(el)));
console.log(diff()); // [ 'a', 'd', 'e', 'f' ]
// OR
const diff2 = () => {
return myArray = myArray.filter((el) => !toRemove.includes(el));
};
console.log(diff2()); // [ 'a', 'd', 'e', 'f' ]
A High performance and immutable solution
Javascript
const excludeFromArr = (arr, exclude) => {
const excludeMap = exclude.reduce((all, item) => ({ ...all, [item]: true }), {});
return arr.filter((item) => !excludeMap?.[item]);
};
Typescript:
const excludeFromArr = (arr: string[], exclude: string[]): string[] => {
const excludeMap = exclude.reduce<Record<string, boolean>>((all, item) => ({ ...all, [item]: true }), {});
return arr.filter((item) => !excludeMap?.[item]);
};
I am attempting to change a program of mine from Python to Javascript and I was wondering if there was a JS function like the Counter function from the collections module in Python.
Syntax for Counter
from collection import Counter
list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a']
counter = Counter(list)
print counter
output
Counter({'a':5, 'b':3, 'c':2})
DIY JavaScript solution:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function Counter(array) {
var count = {};
array.forEach(val => count[val] = (count[val] || 0) + 1);
return count;
}
console.log(Counter(list));
JSFiddle example
Update:
Alternative that uses a constructor function:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function Counter(array) {
array.forEach(val => this[val] = (this[val] || 0) + 1);
}
console.log(new Counter(list));
JSFiddle example
You can use Lo-Dash's countBy function:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
console.log(_.countBy(list));
JSFiddle example
I know I'm late but in case if someone is looking at this in 2020 you can do it using reduce, for example:
const counter = (list) => {
return list.reduce(
(prev, curr) => ({
...prev,
[curr]: 1 + (prev[curr] || 0),
}),
{}
);
};
console.log(counter([1, 2, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 1, 0]));
// output -> { '0': 1, '1': 6, '2': 2, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1 }
more advance example with a callback function and context binding
const data = [1, 2, 3, 4, 5];
const counter = (list, fun, context) => {
fun = context ? fun.bind(context) : fun;
return list.reduce((prev, curr) => {
const key = fun(curr);
return {
...prev,
[key]: 1 + (prev[key] || 0),
};
}, {});
};
console.log(counter(data, (num) => (num % 2 == 0 ? 'even' : 'odd')));
// output -> { odd: 3, even: 2 }
There is also pycollections.js, which works on Node and in client-side JS.
Example:
var collections = require('pycollections');
var counter = new collections.Counter([true, true, 'true', 1, 1, 1]);
counter.mostCommon(); // logs [[1, 3], [true, 2], ['true', 1]]
For those who want a pure JavaScript solution:
function countBy (data, keyGetter) {
var keyResolver = {
'function': function (d) { return keyGetter(d); },
'string': function(d) { return d[keyGetter]; },
'undefined': function (d) { return d; }
};
var result = {};
data.forEach(function (d) {
var keyGetterType = typeof keyGetter;
var key = keyResolver[keyGetterType](d);
if (result.hasOwnProperty(key)) {
result[key] += 1;
} else {
result[key] = 1;
}
});
return result;
}
Therefore:
list1 = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
console.log(countBy(list1)); // {'a':5, 'b':3, 'c':2}
list2 = ['abc', 'aa', 'b3', 'abcd', 'cd'];
console.log(countBy(list2, 'length')); // {2: 3, 3: 1, 4: 1}
list3 = [1.2, 7.8, 1.9];
console.log(countBy(list3, Math.floor)); // {1: 2, 7: 1}
In Python, the Counter also has add and update methods, which are used quite commonly. So a better solution would be this:
function Counter(array) {
this.add = (val) => {
this[val] = (this[val] || 0) + 1;
};
this.update = (array) => {
array.forEach((val) => this.add(val));
};
this.update(array);
}
// Example usage
let myCounter = new Counter([1, 2, 2])
myCounter.update([3, 3, 3])
myCounter.add(4)
console.log(myCounter)
Here is a simple and easy to read solution:
const word1 = "tangram"
const dict1 = {}
for (let char of word1){
console.log(char)
if (dict1[char]){
dict1[char] += 1
}else{
dict1[char]= 1
}
}
This is my solution with explicit function calls
let list = [4, 6, 5, 3, 3, 1];
function counter(list) {
let count = function(n) {
let cnt = 0;
for (let v of list) {
if (v === n) cnt++
}
return cnt
}
let [...listSet] = new Set(list);
let cntr = {};
for (let v of listSet) {
cntr[v] = count(v)
}
return cntr
}
console.log(counter(list))
Another version ->
s = "naman";
const counter = (s, sMap = {}) => {
[...s].map((el) => {
sMap[el] = sMap[el] ? sMap[el] + 1 : 1;
});
return sMap;
};
const res = counter(s);
console.log(`res`, res);
How about this pure functional way:
let list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function counter(array) {
return array.reduce((acc, value, index) => {
acc[value] = value in acc ? acc[value] + 1: 1
return acc;
}, {});
}
Fiddle Link