textbox search through array with keys and display closest results - javascript

So, I'm not sure if there is a better method of what I'm trying to do, but effectively I have an array of about 12,000 elements, and each has a key in the format of:
var arrayObj = {
"blue": 'key1',
"orange": 'key2',
"red": 'key3',
"black": 'key4',
//ect...
}
I have a textbox and using javascript each keypress should check for new results. I'd like to display the top 5 most relevant results from the array based off of what is typed within the textbox. Example, if I type bl the results should be blue and black etc.
$('#searchBox').keypress(function() {
madeSearch();
}
function madeSearch() {
var isInArray = [];
if($.inArray($('#searchBox').value, arrayObj) > -1) {
//get arrayObj entry and key.
}
}
I'm just quite confused because I've never worked with javascript array keys before and I'm not sure how to attain them and what the best practices are for searching through an array of length 12,000...
Any tips or help? Thanks.

I'm not sure if this what you looking for you can use it in local by providing your array or ajax
https://github.com/devbridge/jQuery-Autocomplete
Ajax lookup:
$('#autocomplete').autocomplete({
serviceUrl: '/autocomplete/countries',
onSelect: function (suggestion) {
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
Local lookup (no ajax):
var countries = [
{ value: 'Andorra', data: 'AD' },
// ...
{ value: 'Zimbabwe', data: 'ZZ' }
];
$('#autocomplete').autocomplete({
lookup: countries,
onSelect: function (suggestion) {
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});

To get the keys:
Object.keys(arrayObj)
To perform prefix search in the above key list,
Object.keys(arrayObj).filter(function(key){ return key.substring(0, input.length) === input })
If you want to optimise this further, look into data structures like Trie.
JavaScript Trie Performance Analysis

Related

How to combine JS Objects with duplicate key/values

sorry if this is a easy question, I am just having a hard time trying to figure out how I would tackle this problem.
For example, I have 2 Objects as below:
cont oldCar = {
model: 'Honda',
notes: {
id: 1,
timestamp: 2000,
text: 'is old'
}
}
cont oldCar = {
model: 'Toyota',
notes: {
id: 1,
timestamp: 4000,
text: 'is new'
}
}
I want to try and combine the above two objects. I know they have same key's so I wanted to merge the values of each key if they are the same. Such as:
mode: 'Honda / Toyota'
I tried the following:
let merged = {...obj1, ...obj2};
But this will merge both objects but it only retains the values from the right object. I was trying to do a for loop and add check if the key is same in both objects then combine the values together but I keep getting lost and it is hard to visualise. If someone could help me understand how i can create an for loop to start the comparison that would help me in completing the rest.
To do this merge, perhaps you could do some array-reduce, it will also work with a list of unspecific size:
let array = Array(oldCar1,oldCar2)
let result = array.reduce((a,b)=> {
let r = Object.assign({},a)
r.notes = Object.assign({},r.notes)
if (a.model != b.model) {
r["model"] = a.model + " / " + b.model;
}
if (a.notes.text != b.notes.text) {
r.notes.text = a.notes.text + " / " + b.notes.text;
}
// ...
return r;
})
What exactly do you want to achieve? Is it only the merging of model prop or something else?
Do you have more than two objects, is the amount of objects dynamic? If there are only two objects you can do that without any loops.
const merged = {
model: `${firstCar.model} / ${secondCar.model}`,
// etc...
};
But as I said before - if the amount of objects is not constant then you'd need a map that would:
go through each car
try and find a match by ID from other cars
if there's a match return a merged result, if there's no match return the object as it is
Let me know what exactly are your needs here.

Bootstrap selectpicker val won't accept an object just string

I want to understand one behavior from selectpicker. I'm trying load this this element with one attribute from my session:
Template.checkNotification.onRendered(function () {
var currentNotification = Session.get("currentNotification");
this.$('#symptoms1').selectpicker({
multipleSeparator: ' ',
});
this.$('#symptoms2').selectpicker({
multipleSeparator: ' ',
});
var symptoms = [];
for (var symptom of currentNotification.symptom.symptoms) {
symptoms.push('"' + symptom.name + ';;' + symptom.value + '"');
}
var symptomsSelected = symptoms.join(", ");
var test1 = ['Dor de Cabeça;;dor-de-cabeca','Náusea;;nausea'];
var test2 = "["+symptomsSelected+"]";
this.$('#symptoms1').selectpicker('val', test1);
this.$('#symptoms2').selectpicker('val', test2);
});
When I execute this code, only the element #symptom1 works and loads the proper options into the selectpicker.
How can I build an array of option to provide to selectpicker?
If you want to pass the symptoms to the selectpicker, you need to provide it as a native array. This code builds the array the original way and the new way (suitable for assigning to the val):
var currentNotification = {
symptom: {
symptoms: [
{ name: "symptom1", value: "value1" },
{ name: "symptom2", value: "value2" },
{ name: "symptom3", value: "value3" },
{ name: "symptom4", value: "value4" },
{ name: "symptom5", value: "value5" }
]
}
};
var symptoms = [];
for (var symptom of currentNotification.symptom.symptoms) {
symptoms.push('"' + symptom.name + ';;' + symptom.value + '"');
}
var symptomsSelected = symptoms.join(", ");
var test1 = "["+symptomsSelected+"]";
var test2 = $.map(currentNotification.symptom.symptoms, function(symptom) {
return symptom.name + ';;' + symptom.value;
});
console.log("test1: ", typeof test1, " value: ", test1);
console.log("test2: ", typeof test2, " value: ", test2);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
How does this work? The jQuery $.map function will process each of the symptoms from the currentNotification.symptom.symptoms array, and format them individually for use with selectpicker. Each element is formatted, but the array is left as a native Javascript array, with each element corresponding to the original currentNotification.symptom.symptoms array.
Compare the results of test1 and test2 to see the difference:
test1: string value: ["symptom1;;value1", "symptom2;;value2", "symptom3;;value3", "symptom4;;value4", "symptom5;;value5"]
test2: object value: [
"symptom1;;value1",
"symptom2;;value2",
"symptom3;;value3",
"symptom4;;value4",
"symptom5;;value5"
]
Note that in this example code, test1 is a string, which is formatted like an array. This resembles a JSON array value, not a native Javascript array value. test2 is a Javascript array suitable for use with selectpicker.
Using the $.map solution will solve your current problem and produce a proper Javascript array. Now, you can use this to set the val of the selectpicker:
this.$('#symptoms2').selectpicker('val', test2);
plunker: http://plnkr.co/edit/sjwK1e?p=preview
$scope.symptomsSelected="["+'"\My selected item is string\"'+"]"
$scope.test2 =angular.fromJson($scope.symptomsSelected);
$scope.test2=$scope.test2[0];
{{test2}}
<br>
{{symptomsSelected}}

Twitter typeahead.js and a json object for multiple matches

I'm writing a program that should be able to find a university course based on a few of the courses parameters. I have a huge json object containing 1360 objects, and each object has around 20-30 parameters. I am only interested in about 3-5 of them however.
What I want to do is to be able to find a course based on it's number, the name, the name of the teacher or a set of keywords that each course has in it's description.
Anyway. I have tried messing around with twitters typeahead.js and as far as I can understand you need to parse it an array of strings, and it only takes 1 array as far as it seems. Isn't there an easy way (or another javascript module) that I can do this?
My current object is:
var courses = [
{ "number": "<5 digits>",
"name": "<course name>",
"teacher": "<teacher name>",
"keywords": "<string of keywords>" }
{ ... }
{ ... }
{ ... }
and so forth..
];
And it is as mentioned 1360 objects of size.
I want to get an output similar to this:
However I want to be able to search on the parameters mentioned (number, name, teacher name, keywords) and get the best possible match for whatever was input into the text field.
Does anyone have an idea of how to achieve this?
This is very easy, but not with typeahead.js. It has 231 open issues (at the time typing, some very serious) and has not been updated in a year.
Use bootstrap3-typeahead instead. It is maintained and uptodate. Take a look at the docs before reading further. You are not providing a JSON so I am using another JSON in the example below.
All you have to do is to define a matcher method, here a very simple not case sensitive that compares with all attributes in a JSON item :
matcher: function(item) {
for (var attr in item) {
if (~item[attr].toString().toLowerCase().indexOf(this.query.toLowerCase())) return true
}
return false
}
You can override which attrs / text that should be shown in the dropdown using displayText (pure example) :
displayText: function(item) {
return item.id + ' ' + item.label + ' ' + item.value
}
If you want to override the rendering of the items completely, as complex HTML, use a combination of displayText and highlighter where each display string contain the stringified JSON :
displayText: function(item) {
return JSON.stringify(item)
},
highlighter: function(item) {
item = JSON.parse(item)
return '<span><h4>'+item.value + '</h4>' + item.id + ' ' + item.label +'</span>'
}
When the display string is stringified, the <input> must be updated with the value attribute(s) that should be the selected value :
afterSelect: function(item) {
this.$element[0].value = item.value
}
Complete example :
$('#example').typeahead( {
source: json,
matcher: function(item) {
for (var attr in item) {
if (~item[attr].toString().toLowerCase().indexOf(this.query.toLowerCase())) return true
}
return false
},
displayText: function(item) {
return JSON.stringify(item)
},
highlighter: function(item) {
item = JSON.parse(item)
return '<span><h4>'+item.value + '</h4>' + item.id + ' ' + item.label +'</span>'
},
afterSelect: function(item) {
this.$element[0].value = item.value
}
})
demo -> http://jsfiddle.net/vhme3td1/

Loop Through Multivalue Array

First of all sorry if I have the terminology wrong, if so could you please correct me?
I am trying to loop through the following javascript array.
var pieData2 = [
{
label: 'wow',
value: 30,
color:"#F38630"
},
{
label: 'wow2',
value : 10,
color : "#E0E4CC"
},
{
label: 'wow3',
value : 100,
color : "#69D2E7"
}
];
I am trying to write the label and color into separate divs. I have tried the following concept, but have got no where.
$.each( pieData2[0], function( key, value ) {
alert( key + ": " + value );
});
The jQuery.each() function is designed to iterate over a collection, such as the elements in an array or over the properties of an object. In your case, it's iterating over an array of objects. In your code, pieData2 is your array, so you want to pass that as the first argument to $.each(). The second argument is a function that will handle each iteration, and has two parameters: index and value (though you can name them whatever you wish).
$.each(pieData2, function(index, value) {
// index will be 0, 1, 2
// value will be equivalent to pieData2[0], pieData2[1], pieData2[2]
console.log(value.label); // outputs wow, wow2, wow3
});
As has been used in another answer, you can also use the this keyword to refer to the element being looked at for that iteration, so this and value in the above code are the same.
http://jsfiddle.net/X5r8r/1119/
var pieData2 = [
{
label: 'wow',
value: 30,
color:"#F38630"
},
{
label: 'wow2',
value : 10,
color : "#E0E4CC"
},
{
label: 'wow3',
value : 100,
color : "#69D2E7"
}
];
$.each( pieData2, function( key, value ) {
alert( key + ": " + value['label'] +" value: " + value['value']);
});
each is fine, but you need to loop over pieData2 not over the first element of pieData2...
$.each( pieData2, function() {
alert( this.label + ": " + this.value );
});
http://jsfiddle.net/3anAJ/
Try this
$.each( pieData2, function(index) {
alert("label = " +pieData2[index].label+ " color = " +pieData2[index].color);
});
You do not need jQuery for this. In cases where jQuery really isn't needed, I don't really suggest it. At that point it's just kind of pointless. Use regular JS where possible, use jQuery where needed.
for (var i = 0; i < pieData2.length; i++) {
alert(pieData2[i].label + ' : ' + pieData2[i].value);
}
If you really want to use jQuery, since $.each can iterate over arrays AND objects, you can just use it to iterate over the array and alert each.
This will iterate over each object in the array and alert each key, value pair...
$.each(pieData2, function (key, obj) {
alert(obj.label + ' : ' + obj.value);
});
If you need to iterate over the array and over each object (if you do not know the length), then you can do:
for (var i = 0; i < pieData2.length; i++) {
for (var prop in pieData2[i]) {
if (pieData2[i].hasOwnProperty(prop)) {
alert(prop + ' : ' + pieData2[i][prop]);
}
}
}
or
$.each(pieData2, function(obj) {
$.each(pieData2[obj], function(key, value) {
alert(key + ' : ' + value);
});
});

Iteration Of JSON Array Of Objects Not Working

I have an array of objects that should be looking like this...
[{"Name":"blah","Description":"blah"},{"Name":"blah2","Description":"blah2"}]
Using Javascript/jQuery, how can I get the key/value pairs? I've tried many different ways but to no avail. When I try to get the length, it always returns the character count and iterates through each character and not the actual count of objects? When I run this, it returns an alert for [object Object], [object Object] spelled out by each character....
function DisplayItems(data) {
$.each(data, function () {
$.each(this, function (key, value) {
alert(value);
});
});
}
Now that I look at this, is it not the array of objects I'm expecting? How can I actually return the string so I can actually see what's really in it and maybe go from there?
**EDIT:
This is my function to get the orders (cut out the crap and showing you an alert)... I call jQuery.Ajax and pass the returned data to displayOrders(data). The orders have a composite property of Items containing a list of Item.
function displayOrders(data) {
$('#gdvOrders tbody').empty();
for (var key in data.d) {
alert(data.d[key].Items);
}
This is what I passed to displayItems, what you see in the alert function. I display the Orders in one table (hiding some columns including the Items), and want to display the Items for each order in another table when they select a row in the orders table. In the function shown above I can write...
data.d[key].OrderId
and it will display as normal. How do I display the properties for each item?
The jQuery.Ajax function is set to content-type: 'application/json; charset=utf-8' and this is where I get the orders from...
[WebMethod]
public static List<Order> GetOrdersByDept(Department department, Filter filter, DateTime? dateFrom = null, DateTime? dateTo = null)
{
return OrderLists.GetOrdersByDepartment((Department)department, (Filter)filter, dateFrom, dateTo);
}
See this is working:
data=[{"Name":"blah","Description":"blah"},{"Name":"blah2","Description":"blah2"}]
data.forEach(function(i,j){
console.log("Name :"+i.Name+" Description :"+i.Description);
})
Using JavaScript, simply use the for .. in loop
for(var i = 0; i < data.length; i++) {
var obj = data[i];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
alert(key + " = " + obj[key]);
}
}
}
Fiddle here - http://jsfiddle.net/XWsvz/
Now that I look at this, is it not the array of objects I'm expecting? How can I actually return the string so I can actually see what's really in it and maybe go from there?
If the object is being returned as a string, you can simply alert it. However, if your function is being passed an unknown object, you can always convert it back to a JSON string and alert it so that you can visualize the structure:
function displayItems(data) {
alert(JSON.stringify(data));
...
}
As a sidenote, I changed the first letter in your function to a lowercase letter to match naming conventions for functions in JavaScript.
Looks like you're close:
function DisplayItems(data) {
console.log('data is: ', JSON.stringify(data));
$.each(data, function (key, arrayElement, index) {
console.log('arrayElement ' + index + ' is: ', JSON.stringify(arrayElement));
$.each(arrayElement, function (key, value) {
console.log('key: ' + key + ' val: ' + value);
});
});
}
http://jsfiddle.net/ux9D8/
With your data this gives me the following output:
data is: [{"Name":"blah","Description":"blah"},{"Name":"blah2","Description":"blah2"}]
arrayElement undefined is: {"Name":"blah","Description":"blah"}
key: Name val: blah
key: Description val: blah
arrayElement undefined is: {"Name":"blah2","Description":"blah2"}
key: Name val: blah2
key: Description val: blah2

Categories