Add items to an array skipping duplicates - javascript

I want to add items to an array skipping the duplicates. However for some reason only one item is being added and the second item is not being added. Here is my code.
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = []
var from
function findMessages(messageList) {
return messageList = from;
}
add.map(function(map){
from = map
if(main_messages.find(findMessages) === undefined){
main_messages.push(map)
}
});
console.log(main_messages)
So the expected output should be
['email1#gmail.com', 'email2#gmail.com']
But the output I'm getting in this code is only
['email1#gmail.com']
What am I doing wrong and how can I fix this problem?

Looks like you're missing a = in your return statement of findMessages, so you're basically setting from to messageList instead of comparing. Here's the fixed code
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = []
var from
function findMessages(messageList) {
return messageList === from;
}
add.map(function(map){
from = map
if(main_messages.find(findMessages) === undefined){
main_messages.push(map)
}
});
console.log(main_messages)

Consider using the JavaScript 1.6 / ECMAScript 5 native filter method of an Array in the following way:
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = add.filter(function(v, i, a) {return a.indexOf(v) === i;});
Another solution that should offer better performance O(x) would be to use array.reduce:
main_messages = Object.keys(add.reduce(function (p,c) {return (p[c] = true,p);},{}));
Both solutions will result in messages containing:
["email1#gmail.com", "email2#gmail.com"]
In case you need support for Browsers that don't have this implemented, as always there is a pollyfill offered by Mozilla (see bottom of page)

i think your error in below code
function findMessages(messageList) {
return messageList = from;
}
here i think you return to its parent so it is showing one vale.
for this you need to store messageList = from in a var, then return that variable.
var x = messageList;
return x;

You could implement a uniq function. Which is a specialised form of filter.
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = uniq(add);
function uniq(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
console.log(main_messages)
On the other hand, map will always return an array of the same size, unless of course you muck about with assignments, etc.
map passes each element of an array to a callback function you use to modify the individual element without touching the actual array.
Think of it as 1 in 1 out, and so on, but you get the chance to change what is in the array, not how many things are in there. The idea is that there that the array resulting from map has the same length of the input array, but different content.
Methods that can result in an array of different size are filter and reduce for instance.
So being super-terse you could also do this:
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = add.filter( (el, idx, input) => input.indexOf(el) === idx );
console.log(main_messages)

You can use indexOf function in order to check the duplicate. You can try below way to add items to your array without duplicates.
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = []
add.map(function(map){
if(main_messages.indexOf(map) == -1){
main_messages.push(map)
}
});
console.log(main_messages);

Here is an easy answer: https://stackoverflow.com/a/18328062/5228251
Why do it the hard way, it can be done more easily using javascript filter function which is specifically for this kind of operations:
var add = ['email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
var main_messages = []
main_messages = add.filter( function( item, index, inputArray ) {
return inputArray.indexOf(item) == index;
});
------------------------
Output: ['email1#gmail.com', 'email2#gmail.com']

Depending on your needs, using a Set instead of an array may be what you are looking for:
The Set object lets you store unique values of any type, whether
primitive values or object references.
var add = ['email1#gmail.com', 'email1#gmail.com', 'email2#gmail.com', 'email1#gmail.com'];
// Init from an iterable.
var main_messages = new Set(add);
// Add a new email (not previously in the set).
main_messages.add('email3#gmail.com');
// Try to add an existing email, does nothing.
main_messages.add('email1#gmail.com');
console.log('main_messages:')
main_messages.forEach(msg => console.log('-', msg));
Of course, this option is only viable if you keep the data stored in the Set. It would be quite inefficient to convert it into an array after each insertion.
Set is an ES2015 feature. You may want to check the compatibility table and consider using a polyfill if needed.

Related

Create javascript object dynamic in a foreach loop

I want to create objects in a foreach loop:
I'm starting from this:
data.forEach(function (el) {
var dynamic_var = new Quill(el['editor']);
dynamic_var.on('text-change', logHtmlContent);})
But, dynamic_var is 'overwritten', and I want to remain unique.
I check some html elements, and for each one that I found I want to create a new Object, and execute the Object methods.
In my case the variable get a new object per each iteration, is not a new variable.
Is this what you were looking for?
var quillValueContainer = {};
// ...
data.forEach(function(el) {
quillValueContainer[el] = new Quill(el['editor']);
quillValueContainer[el].on('text-change', logHtmlContent);
});
This will only work if el is a string, or number. Seeing how you are using it like this: el['editor'], makes me thing it's an Object, in which case, you can instead use the indices of the elements.
var quillValueContainer = {}; // [] should also work for indexes
// ...
data.forEach(function(el, index) {
quillValueContainer[index] = new Quill(el['editor']);
quillValueContainer[index].on('text-change', logHtmlContent);
});
Also, I don't know if this is something you need to do, but you can check if the Quill Object has already been initialized and skipping a duplication if it has, by doing:
data.filter(function(el, index){ return !quillValueContainer[index]; }).foreach(...
Or
data.forEach(function(el, index) {
if(quillValueContainer[index]) return;
quillValueContainer[index] = new Quill(el['editor']);
quillValueContainer[index].on('text-change', logHtmlContent);
});

Add values from one array to object with specified key & index

Im using the following code,
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
aSelectedDataSet.push(fnCreateEnt(aProp, oData, oPushedObject));
});
This is aSelectedDataSet values
and this is the values of OData
What I need is that before I do the push is to fill the listTypeGroup & listTypeGroupDescription (with the red arrow ) with values that Are inside the oData -> ListTypeGroupAssigment -> result (listTypeGroup & listTypeGroupDescription) , The index is relevant since I want to add just the value of the index in each iteration (since this code is called inside outer loop and the index determine the current step of the loop) ,How it can be done nicely?
The result contain 100 entries (always) and the a selected data will have 100 entries at the end...
Update :)
Just to be clear In the pic I show the values which is hardcoded for this run but the values can be any values, we just need to find the match between the both objects values...
I mean to find a match between to_ListTypeGroupAssigment in both object (which in this case exist ) and if in oData there is result bigger then one entry start with the matching ...
UPDATE2 - when I try Dave code the following happen for each entry,
This happen in the Jquery.extend line...any idea how to overcome this?
The following hard-coded of Dave:-) work perfect but I need generic code which doesnt refer to specific field name
jQuery.each(aDataSet, function(index, oData) {
oPushedObject = {};
fnCreatePushedEntry(aProperties, oData, oPushedObject);
var result = oData.to_ListTypeGroupAssignment.results[index];
oPushedObject.to_ListTypeGroupAssignment = {
ListTypeGroup: result.ListTypeGroup,
ListTypeGroupDescription: result.ListTypeGroupDescription
};
aSelectedDataSet.push(oPushedObject);
});
Im stuck :(any idea how to proceed here ?what can be wrong with the extend ?
should I use something else ? Im new to jQuery...:)
I think that this happen(in Dave answer) because the oData[key] is contain the results and not the specified key (the keyValue = to_ListTypeGroupAssignment ) which is correct but we need the value inside the object result per index...
var needValuesForMatch = {
ListTypeGroup: 'undefined',
ListTypeGroupDescription: 'undefined',
}
//Just to show that oPushedObject can contain additional values just for simulation
var temp = {
test: 1
};
//------------------This object to_ListTypeGroupAssigment should be filled (in generic way :) ------
var oPushedObject = {
temp: temp,
to_ListTypeGroupAssignment: needValuesForMatch
};
oPushedObject is one instance in aSelectedDataSet
and after the matching I need to do the follwing:
aSelectedDataSet.push(oPushedObject);
Is this what you're after:
OPTION ONE - DEEP CLONE FROM oData TO aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
var objectToClone = oData[childObject]['results'][index];
if(objectToClone)
$.extend(true,currentObject[childObject],objectToClone);
}
});
Here is your data in a fiddle with the function applied: https://jsfiddle.net/hyz0s5fe/
OPTION TWO - DEEP CLONE FROM oData ONLY WHERE PROPERTY EXISTS IN aSelectedDataSet
aSelectedDataSet.forEach(function(currentObject,index){
for (var childObject in currentObject) {
if (! currentObject.hasOwnProperty(childObject))
continue;
if(typeof currentObject[childObject] !== 'object')
continue;
for(var grandChildObject in currentObject[childObject]) {
var objectToClone = oData[childObject]['results'][index][grandChildObject];
if(typeof objectToClone === 'object') {
$.extend(true,currentObject[childObject][grandChildObject],objectToClone);
} else {
currentObject[childObject][grandChildObject] = objectToClone;
}
}
}
Fiddle for option 2: https://jsfiddle.net/4rh6tt25/
If I am understanding you correctly this should just be a small change:
jQuery.each(aDataSel, function(index, oData) {
oPushedObject = {};
fnCreateEnt(aProp, oData, oPushObj);
//get all the properties of oData and clone into matching properties of oPushObj
Object.getOwnPropertyNames(oData).forEach(function(key) {
if (oPushObj.hasOwnProperty(key)) {
//oPushObj has a matching property, start creating destination object
oPushObj[key] = {};
var source = oData[key];
var destination = oPushObj[key];
//can safely assume we are copying an object. iterate through source properties
Object.getOwnPropertyNames(source).forEach(function(sourceKey) {
var sourceItem = source[sourceKey];
//handle property differently for arrays
if (Array.isArray(sourceItem)) {
//just copy the array item from the appropriate index
destination[sourceKey] = sourceItem.slice(index, index + 1);
} else {
//use jQuery to make a full clone of sourceItem
destination[sourceKey] = $.extend(true, {}, sourceItem);
}
});
}
});
aSelectedDataSet.push(oPushedObject);
});
It is unclear what exactly your fnCreateEnt() function returns though. I am assuming it is the populated oPushObj but it's not entirely clear from your question.

How to split a URL string with parameters into an array using JavaScript

I'm trying to break up a string like this one:
fname=bill&mname=&lname=jones&addr1=This%20House&...
I want to end up with an array indexed like this
myarray[0][0] = fname
myarray[0][1] = bill
myarray[1][0] = mname
myarray[1][1] =
myarray[2][0] = lname
myarray[2][1] = jones
myarray[3][0] = addr
myarray[3][1] = This House
The url is quite a bit longer than the example. This is what I've tried:
var
fArray = [],
nv = [],
myarray = [];
fArray = fields.split('&');
// split it into fArray[i]['name']="value"
for (i=0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push(nv[0],nv[1]);
nv.length = 0;
}
The final product is intended to be in 'myarray' and it is, except that I'm getting a one dimensional array instead of a 2 dimensional one.
The next process is intended to search for (for example) 'lname' and returning the index of it, so that if it returned '3' I can then access the actual last name with myarray[3][1].
Does this make sense or am I over complicating things?
Your line myarray.push(nv[0],nv[1]); pushes two elements to the array myarray, not a single cell with two elements as you expect (ref: array.push). What you want is myarray.push( [nv[0],nv[1]] ) (note the brackets), or myarray.push(nv.slice(0, 2)) (ref: array.slice).
To simplify your code, may I suggest using Array.map:
var q = "foo=bar&baz=quux&lorem=ipsum";
// PS. If you're parsing from a-tag nodes, they have a property
// node.search which contains the query string, but note that
// it has a leading ? so you want node.search.substr(1)
var vars = q.split("&").map(function (kv) {
return kv.split("=", 2);
});
For searching, I would suggest using array.filter:
var srchkey = "foo";
var matches = vars.filter(function (v) { return v[0] === srchkey; });
NB. array.filter will always return an array. If you always want just a single value, you could use array.some or a bespoke searching algorithm.
for (var i = 0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push([nv[0],nv[1]]);
}
nv.length = 0; is not required, since you're setting nv in each iteration of the for loop.
Also, use var i in the for-loop, otherwise, you're using / assigning a global variable i, that's asking for interference.

jquery split() issue

Hopefully this is easy for someone.
I have a set of checkboxes with values 1,2,3 etc with the same name attribute (cp_bundle).
I use the following code to get a comma-delimited list of those checkboxes.
var hl_calling_plan_bundle = $('input[name="cp_bundle"]:checked').getCheckboxVal() || "";
jQuery.fn.getCheckboxVal = function(){
var vals = [];
var i = 0;
this.each(function(){
vals[i++] = jQuery(this).val();
});
return vals;
}
if I check the first and third checkboxes, the following will be returned:
1,3
Then, I want to run a test to see whether a particular value (e.g. "3") exists in the the returned variable
But, I can't get past the split of the variable using the following:
var aCallingBundle = hl_calling_plan_bundle.split(",");
This gives the error:
hl_calling_plan_bundle.split is not a function
Any idea what's going on?
hl_calling_plan_bundle is an array. You have to use array operations on it, not string operations.
If you want to know if the value 3 is in the array, then you have to search the array for it. There are many ways to search an array, but since you have jQuery, it's easy to use the .inArray() function:
var index = $.inArray(3, hl_calling_plan_bundle);
if (index != 1) {
// found 3 in the array at index
}
Incidentally, you may want to simplify your function like this:
jQuery.fn.getCheckboxVal = function(){
var vals = [];
this.each(function(){
vals.push(this.value);
});
return vals;
}
or this way:
jQuery.fn.getCheckboxVal = function(){
return(this.map(function(){return(this.value)}).get());
}
split() is a String method, it does not exist on an Array.
When you say the following is returned 1,3, you may be implicitly calling the String's toString() method, which will by default join() the array members with a comma. If you explicitly called toString(), then you could call split(), but that would be an anti pattern.
You don't need to split the string, you can just use RegEx to search:
var str = '1,3,22,5';
/\b1\b/.test(str); // true
/\b2\b/.test(str); // false
/\b3\b/.test(str); // true
/\b5\b/.test(str); // true
/\b22\b/.test(str); // true
Making it a function:
String.prototype.findVal = function(val){
var re = new RegExp('\\b' + val + '\\b');
re.lastIndex = 0;
return re.test(this);
};
str.findVal(2); // false
str.findVal(22); // true
To get the checkboxes:
var cbs = document.getElementsByName('cp_bundle');
To get arrays of all values and the checked values:
var allValues = [];
var checkedValues = [];
for (var i=0, iLen=cbs.length; i<iLen; i++) {
if (cbs[i].checked) checkedValues.push(cbs[i].value);
allValues[i] = cbs[i].value;
}

how to parse array and create new array with parsed values?

I have an array
var data = new Array("1111_3", "1231_54", "1143_76", "1758_12");
now I want to parse data[0] to get 1111.
var ids = new Array();
// example: ids = Array("1111", "1231", "1143", "1758");
and copy all ids from data to ids Array.
is it possible to do it like in php or do i need to use loops?
Thanks.
Really simple:
var ids = [];
for(var i = 0, j = data.length; i < j; ++i) {
var idString = data[i];
ids.push(idString.substring(0, idString.indexOf('_')));
}
elegance:
data.map(function(x){
return x.split('_')[0];
})
This IS part of the ECMA-262 standard.
But, if you care about supporting old outdated sub-par browsers, use jQuery (or whatever other framework you are using; almost all of them define a custom map function):
$.map(data, function(x){
return x.split('_')[0];
})
What you want to do is called a 'map.'
Some browsers support them, but if you want to be safe you can use underscore.js (http://documentcloud.github.com/underscore/)
You'd end up with either:
_(data).map(function(x){
return x.split('_')[0];
});
or
_.map(data, function(x){
return x.split('_')[0];
});
If you have a really big array it may be faster to join it to a string and split the string, rather than using any of the iterative methods to form it one by one.
var data = ["1111_3", "1231_54", "1143_76", "1758_12"];
var ids= data.join(' ').replace(/_\d+/g,'').split(' ');
alert(ids)
/* returned value: (Array)
1111,1231,1143,1758
*/

Categories