Searching through objects in Javascript - javascript

I have my data structured like this:
participants['John Smith'] = {first_name: 'John', last_name: 'Smith', id: '1'}
Now I have autocomplete input boxes where the participants can just type their first names and then their last names in.
Right now what I do is I have a separate structure to house the first names, another one to house the last names. This is also because Bootstrap's Typeahead needs a "source" so I just put source: first_names.
So basically what happens is that the user types in the first few letters of his name and they can autocomplete, then they select the first few letters of the last name and they can autocomplete, and if their first names and last names match then I would autocomplete the rest of the form. (Right now I'm assuming there are no duplicate names).
I was wondering if I am doing the right thing because it seems too excessive. It works, but is there a better way?

You can overload typeahead's methods to do what you need when it searches for a match, below is a basic code example.
So in your case, you can autocomplete the last name if the first name is a single match (and the rest of the form if you think that makes sense, I personally don't)
You could also use the full name as a single entry field and autocomplete both I guess.
$('#searchBox').typeahead(
{
source: function (query, process) {
process(theArrayToProcess);
},
updater: function (item) {
callAFunctionToDoSomethingWithMatchedItem(item);
return item;
},
matcher: function (item) {
if (item.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1) {
return true;
}
},
sorter: function (items) {
return items.sort();
},
highlighter: function (item) {
var regex = new RegExp( '(' + this.query + ')', 'gi' );
return item.replace( regex, "<strong>$1</strong>" );
},
});

So I was checking out Underscore and I stumbled into Lodash. I solved it:
_.uniq(_.pluck(stuff.participants, 'first_name'))
_.uniq(_.pluck(stuff.participants, 'last_name'))
and stuff to that effect. Thanks everyone. I used underscore before but I forgot about it. Guess you really need to code everyday to retain info.
Re: duplicate naming I have to think about it. Last year there was 200 participants, good thing nobody shared the same name. Backend has a unique constraint on 'first_name' and 'last_name'.

Related

How to filter an array from an array

I have the following two datasets:
// This is based on what user input, usually they will just select 1 or 2.
var selected = ['alpha', 'bravo', 'charlie', 'delta', ...]
var list = [
{
"properties":{
"name" : "Example Name",
"tags":{
"multi_select":[
{
"name":"alpha"
},
{
"name":"charlie"
}
]
}
}
},
...
// same same but many more objects with different tags each
]
Yes, it's super nested, but it comes from an API...
Anyway, so what I'm trying to do now is filter() it based on the selected terms, example,
If list tags contain "alpha, charlie" and "alpha" is selected, it would display object, but if "alpha and bravo" is selected, the object won't match.
So with fewer it can match, but when it gets narrowed don't it shouldn't match.
I tried the following:
let res = list.filter(hb => hb.properties.Tags.multi_select.some(tag => tag.name.includes(selected)));
console.log(res);
This code is largely from Javascript array.filter by element in children
So I noticed, if I have selected "charlie" it returns the correct objects(s) that contains tag "charlie", but when I select more than 1, eg "charlie" and "alpha" it returns nothing even though there is suppose to be matching objects.
Any suggestions how I could improve the filtering?
Thanks in advance.
You need to check every selected tag agains some name of the filtering object.
let res = list.filter(hb => selected.every(tag =>
hb.properties.tags.multi_select.some(({ name }) => name === tag)
));
I think the argument of your includes method here shouldn't be an array here, the check should rather be other way around.
let res = list.filter(hb => hb.properties.tags.multi_select.some(tag => selected.includes(tag.name));
Also, one suggestion - It might be more optimal to use Set instead of Array for this use case maybe, if you want to research a bit.
As to why your code is working when you are only having one item, the reason is that the selected array undergoes a string conversion so ["charlie"] becomes charlie which is returned as true as charlie is your first element in the multiselect array. But when you add another item, it happens like this ["alpha", "charlie"] is casted to "alpha,charlie" which is concatenation of two items, so it would naturally return false.

Create a table with alphabetical sorting and search

I wants to create a table (with alphabetical filter and search) like this(example). In which by clicking on any alphabet table shows name that started with only that alphabet. And also have a search option.
I have searched a lot on google but couldnt find table like that. Any one have any idea how i can get that table.
On both letter click and search events, you should run a JS filter function on your data set and display only the relevant data.
A naive example of a filter function:
function filterByProperty(data, prop, searchString) {
return data.filter(function(singleObject) {
return singleObject[prop].indexOf(searchString) === 0;
});
}
var dataSet = [
{
name: "Amanda",
company: "Google"
},
{
name: "Johnny",
company: "Facebook"
},
{
name: "Max",
company: "Go Daddy"
}
];
// Will return a collection containing the first and third objects in dataSet.
var filteredData = filterByProperty(dataSet, "company", "G");
It used a plugin called: DataTable, you can follow the guide here to make your own alphabet search.
https://www.datatables.net/blog/2014-08-26
No enough reputation to comment, so...
From Chrome Developers(Press F12 -> click on Network tab & reload the page) tool
I see dynamic-table.js(not sure if this is library or custom file ) &
jquery.dataTables.jsis used. So you can take a look into these library
I have found solution of my problem through the link provided below.
SOLUTION IS HERE
Note : In this solution only alphabetical filter is available, search feature is not there. Search feature was not that important to me, so I am fine with only alphabet filter.

AngularJS filter in certain fields with "OR" condition

I have an issue with AngularJS filters.
I wan't to "filter" with one input search box, but to make list filtered with multiple fields with "OR" condition.
Here, is a demo.
http://jsbin.com/wodewexetu/2/edit
I wan't to filter the list with only name and description fields.
If I type: "hello", "world", or "angular" it should not return any result.
If I type: "example", it should return only 1st and 3rd rows.
If I type: "description" it should return only 1st and 2nd rows. and etc.
So in general, I want to filter with one name, multiple, but certain fields in list with "OR" condition.
I was able to make the "AND" condition, but it's not what I need.
I also searched a lot about this topic, but unfortunately, non of those codes and examples worked for me.
Please, help and thanks in advance.
You can create your own custom filter, and apply whatever functionality you want.
In your case, something like:
app.filter('customFilter', function() {
return function(items, search) {
if (!search) {
return items;
}
var searchItems = new RegExp(search.split(' ').join("|"), "i");
return items.filter(function(item) {
return searchItems.test(item.name) || searchItems.test(item.description);
});
};
});
See it working here.
I think OR filters are not supported yet. I solved it by creating a meta propery called $searchable.
angular.forEach($scope.items, function (item){
Item.$searchable = [item.name, item.description].join (' ');
}
In the html:
<input type="text" ng-model="search.$searchable">

How do I select a record by matching an index based on a partial string that contains '-' characters?

I'm using YDN-DB (an abstraction on top of IndexedDB) as a local database. I have an object store called 'conversations', and in that store, there's an index called 'participants' where there is a string containing id's for different users in the conversation. For example:
Example Conversation #1:
id: 1234343434353456,
participants: '171e66ca-207f-4ba9-8197-d1dac32499db,82be80e2-2831-4f7d-a8d7-9223a2d4d511'
Example Conversation #2:
id: 4321343434356543,
participants: 'd7fa26b3-4ecc-4f84-9271-e15843fcc83f,171e66ca-207f-4ba9-8197-d1dac32499db'
To try to perform a partial match on an index, I tried using ydn-db-fulltext as a solution. The full text catalog looks like this:
{
name: 'participants',
lang: 'en',
sources: [
{
storeName: 'conversations',
keyPath: 'participants',
weight: 1
}
]
}
I see that the catalog is generated, but there seems to be a problem doing exact matches. For example, if I query using only part of the key in the participants index, I get back a primary key from the catalog:
db.search('participants', 'd7fa26b3').done(function(results) {
if(results.length == 0) console.debug('No results found...');
console.debug(results); // there is 1 object here!
var primaryKey = results[0].primaryKey; // primaryKey exists!
});
However, when using any value past the '-', the search request returns 0 results:
db.search('participants', 'd7fa26b3-4ecc-4f84-9271-e15843fcc83f').done(function(results) {
if(results.length == 0) console.debug('No results found...');
console.debug(results); // there are 0 objects in the array
var primaryKey = results[0].primaryKey; // primaryKey throws undefined since there are 0 results!
});
This makes sense, when reading the documentation, in that '-' and '*' are reserved characters that remove a phrase and match a prefix respectively:
Query format is free text, in which implicit and/or/near logic operator apply for each token. Use double quote for exact match, - to subtract from the result and * for prefix search.
I tried putting double quotes inside the single quotes, using only double quotes, and also escaping all of the '-' characters with a backslash, but none of these seem to work.
So the question is how does one perform a match in an index where the string contains '-' characters?
Have you try db.search('participants', '"d7fa26b3"').
BTW, you are using full text search that is not suppose to do. You have to tokenize your string and index them manually.
If you store the participants field of your object as an array, then you can use the multi-entry flag to the createIndex method called on the participants field, and probably do what you want.
The number of items in the participants property of the object is mutable. When you update an object in the store and it has a different number of items in the partic property, then the index is automatically updated as a result (just like any other index). If you add an item to the prop, then restore (put/override/cursor.update) the object in the store, the index updates.
It helps to review the basics of how a multi-entry index works. You can do this with vanilla js, without a framework, and certainly without full-text searching.

overwrite Backgrid.Extension.LunrFilter

var lunrFilter1 = new Backgrid.Extension.LunrFilter({
collection: pageableTerritories.fullCollection,
placeholder: "Name",
fields: {
name: name
},
ref: 'id',
wait: 150
});
$example2.prepend(lunrFilter1.render().el);
lunrFilter1.$el.css({float: "left", margin: "20px"});
It uses lunr.tokenizer from assest/js/lunr.js.
How can I overwrite/extend it ?
The default tokenizer in lunr is just a function on the top level lunr name space.
Unfortunately there is no easy way to extend the tokeniser at the moment, it is possible to completely replace it with your own implementation though.
lunr.tokenizer = function (obj) {
// your implementation here!
}
The existing implementation handles being passed strings, arrays of strings or nothing (null or undefined) and must return either an array of strings or an empty array. This array will then be processed by the rest of the text processing pipeline. If you satisfy those constraints lunr will be none the wiser that you swapped out the tokenizer and will continue to work as expected.
If there is a specific feature that is missing, or a bug that you have found, please open an issue on the Github project.

Categories