Update or change typeahead data - javascript

I'm using this typeahead library: http://twitter.github.io/typeahead.js/
I managed to create a sample same as they have on their page with having local values.
$( document ).ready(function() {
var globalResults = ["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut","Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas","Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota","Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey","New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon","Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas","Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"];
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
$('#search-input').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'globalResults',
source: substringMatcher(globalResults)
});
});
The problem is when I update the globalResults, the typeahead still shows the same old results Alambama, Alaska...
I'm trying to update them this way, but it doesn't work:
globalResults = results;
var autocomplete = $('#search-input').data('typeahead');
autocomplete.source = globalResults;
Results are retrieved from a server in a string array. I debugged it and it contained the new data and it did copy them into globalResultes and updated it. But somehow this line does nothing:
autocomplete.source = globalResults;
Where might be the problem for which the source for typeahead is not updating?
My html looks like this:
<span style="position: relative; display: inline-block;" class="twitter-typeahead">
<input type="text" spellcheck="true" class="form-control typeahead tt-input" placeholder="Search for science, search for data ..." id="search-input" autocomplete="off"></input>
</span>

The only way I was able to update the datasource used is by using BloodhoundJS with TypeaheadJS.
I initialize a local source:
var globalResults = ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Dakota", "North Carolina", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"];
and an alternative local source:
var otherResults = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
I create a bloodhound instance and set it to a local data source:
var states = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: globalResults
});
I create a method to switch between the sources. In this case I use a button and on click, make the change:
$(document).on('click', '#changeSource', function() {
console.log('change the data');
states.clear();
states.local = otherResults;
states.initialize(true);
});
This will work for remote data as well.
Here is a fiddle demonstration.

this will clear current datums and reload the data:
states.clear();
states.clearPrefetchCache();
states.clearRemoteCache();
states.initialize(true);

Related

Google Maps is undefined when I reload the page due to script loading issues, how can I fix this?

I've implemented the Google Maps API onto my page and it works perfectly except when I do a refresh of the screen.
Once I hit refresh, I get this type error:
Cannot read property of maps, undefined
Specifically, it's happening around line 28 in the Map class.
This is where I'm setting the script for my API and when I inspect the page I can see that it is present (even when I refresh the page):
class App extends Component {
// Render a script tag for scriptUrl in head of the HTML page
addScriptToPage(scriptUrl) {
const script = document.createElement("script");
script.src = scriptUrl;
document.head.appendChild(script);
}
componentDidMount() {
// CreateReactApp requires the REACT_APP_ prefix for env vars
let MAPS_API_KEY = process.env.REACT_APP_MAPS_API_KEY;
// place the google maps api as a script tag in the head
// this script places a google object on window.google
let mapsApiUrl = `https://maps.googleapis.com/maps/api/js?key=${MAPS_API_KEY}`;
this.addScriptToPage(mapsApiUrl);
}
render() {
return (
<div className='app-wrapper'>
<Modal />
<NavBarContainer />
<div className="app-main-content-wrapper">
<Switch>
<Route exact path='/' component={SpotIndex} />
<Route exact path='/spots/:spotId' component={SpotDetail} />
<Route exact path='/search/:find_loc' component={SearchContainer} />
</Switch>
</div>
</div>
);
}
};
And this is my Map class:
class Map extends Component {
constructor(props) {
super(props);
}
// Initially mounts the map with first search input
componentDidMount() {
this.drawMap(this.props.find_loc);
}
componentWillReceiveProps(newProps) {
this.drawMap(newProps.find_loc);
}
drawMap(address) {
// Solves problem of linting rule in ReactJS that forbids unknown global vars
const google = window.google;
const map = document.getElementById('map-container');
// Creating the map
MapUtil.setOptionsFromLocation(address)
.then(options => {
this.map = new google.maps.Map(map, options);
// before adding markers, set up bounds
let bounds = new google.maps.LatLngBounds();
// Displaying nearby spots
this.props.spots.forEach(spot => {
// Create a position from spot coordinates
const latitude = spot.latitude;
const longitude = spot.longitude;
const position = new google.maps.LatLng(latitude, longitude);
// Place markers on the map
const marker = new google.maps.Marker({
position,
map: this.map
});
// extend the bounds to fit this position
bounds.extend(position);
});
// Autozooming and autocentering if there are results
if (this.props.spots.length) {
// Autozooming
this.map.fitBounds(bounds);
// Adjust zooming value if too large
let theMap = this.map;
var listener = google.maps.event.addListener(theMap, "idle", function () {
if (theMap.getZoom() > 16) theMap.setZoom(16);
google.maps.event.removeListener(listener);
});
// Autocentering
this.map.panToBounds(bounds);
}
});
}
// Renders Map component
render() {
return <div id="map-container"></div>;
}
}
Per the suggestions below, it appears that the error is not in the above components. Doing some digging, I did find another place where I'm calling the Map component.
This is a general search component that renders both the Map and the SearchIndex (the search results):
class Search extends React.Component {
constructor(props) {
super(props);
this.state = { find_loc: this.props.find_loc };
}
// Triggers a re-render when component receives new props
componentWillReceiveProps(newProps) {
this.setState({ find_loc: newProps.match.params.find_loc });
}
// Gets spots matching search input
getSpots(spots) {
// Library of state abbreviations
const states = {
"AL": "alabama",
"AK": "alaska",
"AS": "american samoa",
"AZ": "arizona",
"AR": "arkansas",
"CA": "california",
"CO": "colorado",
"CT": "connecticut",
"DE": "delaware",
"DC": "district of columbia",
"FL": "florida",
"GA": "georgia",
"GU": "guam",
"HI": "hawaii",
"ID": "idaho",
"IL": "illinois",
"IN": "indiana",
"IA": "iowa",
"KS": "kansas",
"KY": "kentucky",
"LA": "louisiana",
"ME": "maine",
"MD": "maryland",
"MA": "massachusetts",
"MI": "michigan",
"MN": "minnesota",
"MS": "mississippi",
"MO": "missouri",
"MT": "montana",
"NE": "nebraska",
"NV": "nevada",
"NH": "new hampshire",
"NJ": "new jersey",
"NM": "new mexico",
"NY": "new york",
"NC": "north carolina",
"ND": "north dakota",
"OH": "ohio",
"OK": "oklahoma",
"OR": "oregon",
"PA": "pennsylvania",
"PR": "puerto rico",
"RI": "rhode island",
"SC": "south carolina",
"SD": "south dakota",
"TN": "tennessee",
"TX": "texas",
"UT": "utah",
"VT": "vermont",
"VI": "virgin Islands",
"VA": "virginia",
"WA": "washington",
"WV": "west Virginia",
"WI": "wisconsin",
"WY": "wyoming"
}
// Getting lowercased location string from search input
const location = this.state.find_loc;
let locationArr;
let lowercasedLocation = [];
let foundSpots = [];
if (location) {
locationArr = location.split(' ');
locationArr.forEach(word => lowercasedLocation.push(word.toLowerCase()));
lowercasedLocation = lowercasedLocation.join(' ');
}
// Searching for spots matching location
spots.forEach(spot => {
const city = spot.city.toLowerCase();
const stateAbbr = spot.state.toLowerCase();
const stateFull = states[spot.state];
if (city === lowercasedLocation || stateAbbr === lowercasedLocation || stateFull === lowercasedLocation) {
foundSpots.push(spot);
}
});
return foundSpots;
}
// Renders Search component
render() {
const { spots } = this.props;
const foundSpots = this.getSpots(spots);
return (
<div className="search-container">
<div className="search-results-wrapper">
<SearchIndex spots={foundSpots} find_loc={this.state.find_loc} fetchSpots={this.props.fetchSpots} />
<Map spots={foundSpots} find_loc={this.state.find_loc} />
</div>
<Footer />
</div>
);
}
}
I had a similar loading issue with SearchIndex earlier, due to the spots not being fetched when the page reloaded. But I solved it by explicitly telling the SearchIndex component to fetch all spots when the component was mounted; I thinking that perhaps it might be a similar issue for the Map (specifically window.google)?
Add a listener for the tilesloaded event:
this.map = new google.maps.Map(map, options);
this.map.addListener('tilesloaded', function () {
...

jQuery: How do I deal with self-closing tags and multiple loops?

I have 3 arrays: one with a list of cities, one with a list of states (which correspond to the cities), and one that's just a list of states with the duplicates removed.
I'm trying to generate a list that looks like this:
State 1
City 1
City 2
State 2
City 3
Here's what I've got:
$.each(stateArrayUnq, function(i) {
$('#list').append("<li>" + stateArrayUnq[i] + "<ul>");
$.each(stateArray, function(j) {
if (stateArray[j] == stateArrayUnq[i]) {
$('#list').append("<li>" + cityArray[j] + "<\/li>");
}
});
$('#list').append("<\/ul><\/li>");
});
I know I can't structure my code like this without having the browser auto-close my tags prematurely, but unfortunately I don't have the slightest idea how to rebuild this. I've read through a few related threads, but I'm still pretty confused. I think I'm supposed to set the "append" code to variables or something, but I don't know how to handle the loops.
I appreciate any help you can give. Thanks a bunch!
UPDATE: Here are my arrays:
var cityArray = ["Concord", "Lafayette", "Lewisville", "Madison", "NW Freeway Houston", "North Miami", "Casselberry", "South Fort Myers", "SW Freeway", "Woodbury", "Tarpon Springs"]
var stateArray = ["North Carolina", "Louisiana", "Texas", "Wisconsin", "Texas", "Florida", "Florida", "Florida", "Texas", "Minnesota", "Florida"]
var statearrayUnq = ["Florida", "Louisiana", "Minnesota", "North Carolina", "Texas", "Wisconsin"]
You can do this by building an html string which is stored in a variable. When you are done with your logic you can append the build html string.
//Test data
var cityArray = ["Concord", "Lafayette", "Lewisville", "Madison", "NW Freeway Houston", "North Miami", "Casselberry", "South Fort Myers", "SW Freeway", "Woodbury", "Tarpon Springs"];
var stateArray = ["North Carolina", "Louisiana", "Texas", "Wisconsin", "Texas", "Florida", "Florida", "Florida", "Texas", "Minnesota", "Florida"];
var stateArrayUnq = ["Florida", "Louisiana", "Minnesota", "North Carolina", "Texas", "Wisconsin"];
//Generate the html string and append it.
var html = "";
$.each(stateArrayUnq, function(i) {
html += "<li>" + stateArrayUnq[i] + "<ul>";
$.each(stateArray, function(j) {
if (stateArray[j] == stateArrayUnq[i]) {
html += ("<li>" + cityArray[j] + "<\/li>");
}
});
html += "<\/ul><\/li>";
});
$('#list').append(html);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<p>test</p>
</div>
If list were a ul, you could nest them using actual jQuery objects like so.
var cityArray = ["Concord", "Lafayette", "Lewisville", "Madison", "NW Freeway Houston", "North Miami", "Casselberry", "South Fort Myers", "SW Freeway", "Woodbury", "Tarpon Springs"];
var stateArray = ["North Carolina", "Louisiana", "Texas", "Wisconsin", "Texas", "Florida", "Florida", "Florida", "Texas", "Minnesota", "Florida"];
var data = cityArray.map((c,i) => ({state: stateArray[i], city: c})); //flatten the data
var $list = data.reduce(($ul, item) => {
var $cityLi = $(`<li>${item.city}</li>`); //Create <li> for city
var $stateLi = $ul.children(`li[data-state='${item.state}']`); //Find state <li>
if (!$stateLi.length) //Create state <li> if it doesn't yet exist
$stateLi = $(`<li data-state='${item.state}'>${item.state}<ul></ul></li>`);
$stateLi.children("ul").append($cityLi).end().appendTo($ul); //Add the city to the state
return $ul;
}, $("<ul />"));
$("body").append($list);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Selection .val has strange, in-understandable value (Chosen plugin)

In Ruby-on-Rails, I am using the Chosen plugin on a multiselector for a list of provinces as below:
<%= select_tag :provinces,
options_for_select(DataHelper::all_provinces_captions.zip(DataHelper::all_provinces_ids)),
{:multiple => true, class: 'chosen-select chzn-select',
:data => {:placeholder => 'Filter Provinces/States'}}%>
I also have a selector in a form field on the same page like so:
<%= f.select :province_ids,
(DataHelper::all_provinces_captions.zip(DataHelper::all_provinces_ids)),
{ include_blank: true }, {
multiple: true, data: {placeholder: 'Filter Provinces/States'} }
%>
Finally, I have a Javascript function that synchronizes the two when #provinces, with class .chzn-select changes:
var selectedVals = [];
$(".chzn-select").chosen().change(function() {
$("#provinces option:selected").each(function () {
console.log ("this value is " + ($(this).val));
selectedVals.push($(this).val);
});
$("#education_plan_province_ids").empty();
for (var i = 0; i < selectedVals.length; i++) {
console.log (selectedVals[i] + " selected");
$("#education_plan_province_ids").append($("<option>" + selectedVals[i] + "</option>")).prop("selected", true);
}
});
However, in my console, instead of getting an output, "this value is alabama" for example, I get the following:
this value is function (a){var b,c,d,e=this[0];{if(arguments.length)return
d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d
a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e
e+="":n.isArray(e)&&(e=n.map(e,function(a){return
null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase(
],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return
b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!=
(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c
c.replace(bc,""):null==c?"":c)}}
So not surprisingly, I am very confused as to why this is happening!
For a final piece of info, here is all_provinces_captions, all_provinces_ids, canada_provinces_with_caption, and usa_provinces_with_caption in the DataHelper, all arrays:
def self.usa_provinces_with_caption
[["Alabama", "alabama"], ["Alaska", "alaska"], ["Arizona", "arizona"], ["Arkansas", "arkansas"], ["California", "california"], ["Colorado", "colorado"], ["Connecticut", "connecticut"], ["Delaware", "delaware"], ["District Of Columbia", "district of columbia"], ["Florida", "florida"], ["Georgia", "georgia"], ["Hawaii", "hawaii"], ["Idaho", "idaho"], ["Illinois", "illinois"], ["Indiana", "indiana"], ["Iowa", "iowa"], ["Kansas", "kansas"], ["Kentucky", "kentucky"], ["Louisiana", "louisiana"], ["Maine", "maine"], ["Maryland", "maryland"], ["Massachusetts", "massachusetts"], ["Michigan", "michigan"], ["Minnesota", "minnesota"], ["Mississippi", "mississippi"], ["Missouri", "missouri"], ["Montana", "montana"], ["Nebraska", "nebraska"], ["Nevada", "nevada"], ["New Hampshire", "new hampshire"], ["New Jersey", "new jersey"], ["New Mexico", "new mexico"], ["New York", "new york"], ["North Carolina", "north carolina"], ["North Dakota", "north dakota"], ["Ohio", "ohio"], ["Oklahoma", "oklahoma"], ["Oregon", "oregon"], ["Pennsylvania", "pennsylvania"], ["Rhode Island", "rhode island"], ["South Carolina", "south carolina"], ["South Dakota", "south dakota"], ["Tennessee", "tennessee"], ["Texas", "texas"], ["Utah", "utah"], ["Vermont", "vermont"], ["Virginia", "virginia"], ["Washington", "washington"], ["West Virginia", "west virginia"], ["Wisconsin", "wisconsin"], ["Wyoming", "wyoming"]]
end
def self.canada_provinces_with_caption
[["Alberta", "alberta"], ["British Columbia", "british columbia"], ["Manitoba", "manitoba"], ["New Brunswick", "new brunswick"], ["Newfoundland", "newfoundland"], ["Northwest Territories", "northwest territories"], ["Nova Scotia", "nova scotia"], ["Nunavut", "nunavut"], ["Ontario", "ontario"], ["Prince Edward Island", "prince edward island"], ["Quebec", "quebec"], ["Saskatchewan", "saskatchewan"], ["Yukon", "yukon"]]
end
def self.all_provinces_captions
usa_provinces_with_caption.map { |x| x.first } + canada_provinces_with_caption.map { |x| x.first }
end
def self.all_provinces_ids
usa_provinces_with_caption.map { |x| (Province.find_by name: x.first).id} + canada_provinces_with_caption.map { |x| (Province.find_by name: x.first).id }
end
It looks like your missing the parenthesis for the .val method. Try the following:
$(this).val()
Try, on your rendered page, inspect the chosen element and check the element (it usually is hidden) and pick the id there to replace
$(".chzn-select").chosen().change(function() {
}
for
$("#your-id").change(function() {
// do some stuff
$("#your-id").trigger("liszt:updated"); //This is necessary when you change your chosen select options, so it refresh the component
}
Hope it helps!

TypeAhead and Bloodhound only searching on first words in JSON array

I'm using TypeAhead to provide autocomplete results for a textbox. But it seems either Bloodhound or TypeAhead is only searching on the first words of found in the JSON array I'm providing.
The JSON looks as follows:
[
{
"name": "ALICE",
"value": "ALICE",
"physical_address": "ERF 270 CNR. OF THOMPSON AND INTWANA STREET, ALICE",
"province": "PROVINCE",
"tokens": [
"ALICE",
"PROVINCE",
"ERF",
"270",
"CNR.",
"OF",
"THOMPSON",
"AND",
"INTWANA",
"STREET",
"ALICE"
]
},
{
"name": "BUTTERWORTH",
"value": "BUTTERWORTH",
"physical_address": "SHOP NO. 1 MASONIC SHOPPING COMP, COR HIGH & BELL STREET BUTTERWORTH 4960",
"province": "PROVINCE",
"tokens": [
"BUTTERWORTH",
"PROVINCE",
"SHOP",
"NO.",
"1",
"MASONIC",
"SHOPPING",
"COMP",
"COR",
"HIGH",
"&",
"BELL",
"STREET",
"BUTTERWORTH",
"4960"
]
}
]
I'm intialising TypeAhead as follows:
HTML:
<div class="films">
<input type="text" class="form-control typeahead" placeholder="Search" name="films" autocomplete="off" id="search">
</div>
Javascript:
/* TypeAhead invoked */
var _prefetch_url = $root_path + '/media/mod_storelocator/stores_json.php';
// constructs the suggestion engine
var films = new Bloodhound({
datumTokenizer: function (d) {
return Bloodhound.tokenizers.whitespace(d.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 15,
prefetch: _prefetch_url
});
// kicks off the loading/processing of `local` and `prefetch`
films.initialize();
$('.films .typeahead').typeahead(null, {
displayKey : 'value',
source: films.ttAdapter(),
templates: {
suggestion: Handlebars.compile([
'<p class="repo-language">{{province}}</p>',
'<p class="repo-name">{{name}}</p>',
'<p class="repo-description">{{physical_address}}</p>'
].join(''))
}
});
I'd really appreciate any help/pointers.
You were passing in d.value into the datumTokenizer, so it was using just the value object from each element in the array.
Note also that Bloodhound tokenizes the data itself, so you don't need to pass back the individual tokens in your JSON (i.e. you don't need the tokens object).
So I would change it to use Bloodhound to tokenize the value and the physical_address objects combined of each element in the array:
// constructs the suggestion engine
var films = new Bloodhound({
datumTokenizer: function(d) {
return Bloodhound.tokenizers.whitespace(d.name);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 15,
prefetch: {
url: _prefetch_url,
filter: function(list) {
return $.map(list,
function(element) {
return { name: element.value + " " + element.physical_address };
});
}
}
});
Have only been able to test this in a local context with this fiddle, but the prefetch should work the same way.

Add only unique objects to an array in JavaScript

Let's say I start with this:
var shippingAddresses = [
{
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201",
"country": "US"
},
{
"firstname": "Dan",
"lastname": "Hess",
"address1": "304 Riversedge Dr",
"address2": "",
"city": "Saline",
"state": "MI",
"zip": "48176",
"country": "US"
}
]
I use this to prepopulate a form.
Users can edit entries or add new ones. I need to prevent them from adding duplicates.
The issue is that the structure of the form that I am serializing and the order these values are returned from the database are not the same, so there is a chance that I will insert an item into this array with the following format:
{
"country": "US",
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"zip": "22201",
"city": "Arlington",
"state": "VA"
}
Which is the same as the first entry, just ordered differently.
I am loading underscorejs, so if there's a way to handle it with that library that would be great. I'm also using jQuery if that helps.
At this point I'm not sure how to proceed.
The Underscore findWhere function does exactly what you need - it's not an indexOf search by object identity, but searches objects whose properties have the same values as the input.
if (_.findWhere(shippingAddresses, toBeInserted) == null) {
shippingAddresses.push(toBeInserted);
}
Basic example using lodash union method:
var a = [1,2,3];
// try to add "1" and "4" to the above Array
a = _.union(a, [1, 4]);
console.log(a);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.min.js"></script>
While this doesn't directly answers the question, it does answers the broader question of how to add unique values to an Array, and like myself, others might stumble upon this page from google.
based on this answer to: "js-remove-an-array-element-by-index-in-javascript"
https://stackoverflow.com/a/7142909/886092
I'm using the following idiom that is concise and does not require underscore.js or any other framework.
Here is an example for recording selected and deselected rows for DataTables jquery plugin.
I keep an array of currently selected ids, and I don't want to end up with duplicates in the array:
in coffeescript
fnRowSelected: (nodes) ->
position = $selected.indexOf(nodes[0].id)
unless ~position
$selected.push nodes[0].id
return
fnRowDeselected: (nodes) ->
position = $selected.indexOf(nodes[0].id)
if ~position
$selected.splice(position, 1)
More generally it would
position = myArray.indexOf(myval)
unless ~position
myArray.push myVal
or in JS
var position;
position = myArray.indexOf(myval);
if (!~position) {
myArray.push(myVal);
}
If you want to check the user input object you could try this function:
var uniqueInput = {
"country": "UK",
"firstname": "Calvin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201"
};
var duplicatedInput = {
"country": "US",
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201"
};
var shippingAddresses = [{
"firstname": "Kevin",
"lastname": "Borders",
"address1": "2201 N Pershing Dr",
"address2": "Apt 417",
"city": "Arlington",
"state": "VA",
"zip": "22201",
"country": "US"
}, {
"firstname": "Dan",
"lastname": "Hess",
"address1": "304 Riversedge Dr",
"address2": "",
"city": "Saline",
"state": "MI",
"zip": "48176",
"country": "US"
}];
function checkDuplication(checkTarget,source){
_.each(source,function(obj){
if(_.isEqual(checkTarget,obj)){
alert("duplicated");
}
});
}
And try to invoke this check function in different parameter (uniqueInput and duplicatedInput)
I think it could check the duplication input in your shipping addresses.
checkDuplication(uniqueInput,shippingAddresses);
checkDuplication(duplicatedInput,shippingAddresses);
I make a jsfiddle. You could try it.
Hope this is helpful for you.
EDIT, this will work with your example of unsorted properties:
var normalized_array = _.map(shippingAddresses, function(a){
var o = {};
_.each(Object.keys(shippingAddresses[0]), function(x){o[x] = a[x]});
return o;
})
var stringy_array = _.map(normalized_array, JSON.stringify);
shippingAddresses = _.map(_.uniq(stringy_array), JSON.parse});
and we could do this with a one-liner but it would be super ugly:
shippingAddresses_uniq = _.map(_.uniq(_.map(_.map(shippingAddresses, function(a){ var o = {}; _.each(Object.keys(shippingAddresses[0]), function(x){o[x] = a[x]}); return o; }), JSON.stringify)), JSON.parse});
I think you need this,
NOTE: No library is required.
let array = [{ id: 1}, {id: 2}, {id: 3}];
function addUniqeObj(data) {
let index = -1;
for(let i = 0, i < array.length; i++) {
if(array[i].id === data.id) {
index = i;
}
}
if(index > -1) {
array[index] = data;
} else {
array.push(data)
}
}
Basic example using Set() from ECMAScript 2015 (no library required)
The Set object lets you store unique values of any type (whether primitive values or object references). If an iterable object is passed, all of its elements will be added to the new Set. Here I'll just add one value:
// original array with duplicates already present
const numbers = [1, 1, 1, 2, 3, 100]
// Use Set to remove duplicate elements from the array
// and keep your new addition from causing a duplicate.
// New value (100) is not added since it exists (and array
// also is de-duped)
console.log(Array.from(new Set([...numbers, 100])))
// [1, 2, 3, 100]
// New, non-existing value (101) is added (and array is de-duped)
console.log(Array.from(new Set([...numbers, 101])))
// [1, 2, 3, 100, 101]

Categories

Resources