How to search and retrieve array of results from KnockoutObservableArray<string> - javascript

I have below KnockoutObservableArray. I want to search and retrieve the only first 5 results from this array.
var people: KnockoutObservableArray<string>;
this.people.push("abc");
this.people.push("aaa");
this.people.push("xyz");
...
I tried multiple options using people.filter method but could not find exactly what i required.
Can someone please help me here.

Not totally sure what you mean by searching the array. If you want to apply some sort of test to the array items and only retrieve the items that pass then ko.utils.arrayFilter is the way to go.
An example that filters an array and only returns values that match the value held in another observable:
self.KeyWorkerFilter = ko.observable('');
self.KeyWorkersFiltered = ko.computed(function () {
// if the filter is blank then just return the whole collection
if (!self.KeyWorkerFilter || self.KeyWorkerFilter() === '') {
return self.TpwKeyWorkers();
}
var filteredKeyWorkers = ko.utils.arrayFilter(self.TpwKeyWorkers(), function (item) {
var name = item.name.toLowerCase();
return name.includes(self.KeyWorkerFilter().toLowerCase());
});
return filteredKeyWorkers;
});
so in your case you might be doing something like
var.peopleFilter = ko.computed(function() {
return ko.utils.arrayFilter(this.people, function(item) {
// whatever your test is
// example: only return array items that are equal to "aaa"
item === "aaa";
});
});

You can create a computed property like this which will filter the array based on the searchText. Then use slice to get the first 5 results:
Search for "a" in the input and you'll only be able to see the results till "ae".
let viewModel = function() {
let self = this;
self.people = ko.observableArray(["aa", "ab", "ac", "ad", "ae", "af", "ag"]);
self.searchText = ko.observable('');
// When "searchText" or "people" changes, this gets computed again
self.filteredPeople = ko.computed(function() {
if (!self.searchText() || !self.searchText().trim()) {
return [];
} else {
return self.people()
.filter(p => p.toLowerCase().indexOf(self.searchText().toLowerCase()) > -1)
.slice(0, 5);
}
});
};
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="value: searchText, valueUpdate: 'keyup'" placeholder="Search...">
<div data-bind="foreach:filteredPeople">
<span data-bind="text:$data"></span><br>
</div>

Related

Why I do not get all the valuenya when in for loop [duplicate]

I have a set of checkboxes and want to concatenate only the checked ones into a query string, but for some reason val() only returns the first one. The jQuery API docs mention that selects can return an array form the .val() function, can I get this same behavior for inputs as well, with a prototype or jquery plugin?
Here's my code:
$('.boxes input:checked');
// [ <input type="checkbox" value="val1">, <input type="checkbox" value="val2">, <input type="checkbox" value="val3"> ]
$('.boxes input:checked').val();
// "val1"
Ideally the second console output would be:
$('.boxes input:checked').val();
// [ "val1", "val2", "val3" ]
val returns an array of selected values for select elements that have multiple attribute, for other elements it returns the value of the first element in the set. You can use the map method:
var values = $('.boxes input:checked').map(function() {
return this.value;
}).get();
You can also define a method:
$.fn.vals = function() {
var first = this.get(0);
if ( this.length === 0 ) return [];
if ( first.tagName === 'SELECT' && first.multiple ) {
return this.val();
}
return this.map(function() { return this.value; }).get();
}
// ...
var values = $('.boxes input:checked').vals();

Lodash help on filtering using a search term and multiple property names

I wish to search on multiple columns however all the code I could find on the internet was restricted on a single search term that would search multiple columns. I wish to filter on multiple columns by multiple search terms
data:
var propertynames = ['firstName','lastName'];
var data = [
{
"city":"Irwin town",
"address":"1399 Cecil Drive",
"lastName":"Auer",
"firstName":"Wanda"
},
{
"city":"Howell haven"
"address":"168 Arnoldo Light"
"lastName":"Balistreri",
"firstName":"Renee"
}
];
var searchTerm = 'Wanda Auer';
Should result in an array that filtered out the 2nd object.
Thanks!
I've created two solutions for your question. The first one is do exactly what you need: filters collection by two fields. The second one is more flexible, because it allows filter by any multiple fields.
First solution:
function filterByTwoFields(coll, searchFilter) {
return _.filter(coll, function(item) {
return (item.firstName + ' ' + item.lastName) === searchTerm;
});
}
var data = [
{
"city":"Irwin town",
"address":"1399 Cecil Drive",
"lastName":"Auer",
"firstName":"Wanda"
},
{
"city":"Howell haven",
"address":"168 Arnoldo Light",
"lastName":"Balistreri",
"firstName":"Renee"
}
];
var searchTerm = 'Wanda Auer';
var result = filterByTwoFields(data, searchTerm);
alert(JSON.stringify(result));
<script src="https://raw.githubusercontent.com/lodash/lodash/master/dist/lodash.min.js"></script>
Second solution:
function filterByMultipleFields(coll, filter) {
var filterKeys = _.keys(filter);
return _.filter(coll, function(item) {
return _.every(filterKeys, function(key) {
return item[key] === filter[key];
});
});
}
var data = [
{
"city":"Irwin town",
"address":"1399 Cecil Drive",
"lastName":"Auer",
"firstName":"Wanda"
},
{
"city":"Howell haven",
"address":"168 Arnoldo Light",
"lastName":"Balistreri",
"firstName":"Renee"
}
];
var filter = {
firstName: 'Wanda',
lastName: 'Auer'
}
var result = filterByMultipleFields(data, filter);
alert(JSON.stringify(result));
<script src="https://raw.githubusercontent.com/lodash/lodash/master/dist/lodash.min.js"></script>
Not the most efficent but it does the job. You might want to make an non case sensitive comparison on the property values:
var searchTerm = 'Wanda Auer',
splitted = searchTerm.split(' ');
var result = data.filter(function(item){
return window.Object.keys(item).some(function(prop){
if(propertynames.indexOf(prop) === -1)
return;
return splitted.some(function(term){
return item[prop] === term;
});
});
});
https://jsfiddle.net/3g626fb8/1/
Edit: Just noticed the Lodash tag. If you want to use it, the framework is using the same function names as the array prototype, i.e. _.filter and _.some
Here is a pretty straightforward lodash version:
var matchingRecords = _.filter(data, function (object) {
return _(object)
.pick(propertynames)
.values()
.intersection(searchTerm.split(' '))
.size() > 0;
})
It filters the objects based on if any of the chosen property name values intersect with the search term tokens.

Check Checkboxes from an array of objects on load

i have a model like this
function ViewModel(){
var self = this
self.Choices = ko.observableArray([])
self.AcceptedChoices = ko.observableArray([])
self.LoadData = function(){
self.ViewAnswered()
}
self.ViewAnswered = function(){
var url = 'QuestionsApi/ViewAnswered'
var type = 'GET'
ajax(url , null , self.OnViewAnsweredComplete, type )
}
self.OnViewAnsweredComplete = function(data){
var currentAnswer = data.Answer
self.Choices(currentAnswer.Choices)
self.AcceptedChoices(currentAnswer.AcceptedChoices)
}
self.LoadData()
}
Here is my object. I have removed extra things
{
"AcceptedChoices": [94, 95],
"Choices": [{
"ChoiceId": 93,
"ChoiceText": "Never"
}, {
"ChoiceId": 94,
"ChoiceText": "Sometimes"
}, {
"ChoiceId": 95,
"ChoiceText": "Always"
}]
}
And here is binding
<u data-bind="foreach:Choices">
<li>
<input type="checkbox" name="choice[]" data-bind="value:ChoiceId,checked:$root.AcceptedChoices">
<span data-bind="text:ChoiceText">Never</span>
</li>
</u>
Now the problem is that checkboxes are not being checked due to the choices being array of objects. How can i resolve this issue? Although the same thing works for radio where there is only one selection.
Never mind i have found a solution here
checked binding does not properly compare primatives
Also it tells two ways for this. The Solution provided in fiddle is creepy so i will use the one using knockout version 3.0.0.
All i need to do is attach knockout-3.0.0.js instead of any other and then use checkedValue instead of value.
<input type="checkbox" name="choice[]"
data-bind="
checkedValue:ChoiceId,
checked:$root.AcceptedChoices"
>
And that's done. Hope it helps someone.
EDITS :
I noticed it is not working on the Chrome. So i found an alternative. I created these two functions.
self.ConvertToString = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(item.toString())
})
return AcceptedChoices
}
self.ConvertToInteger = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(parseInt(item))
})
return AcceptedChoices
}
And use them
self.AcceptedChoices(self.ConvertToString(currentAnswer.AcceptedChoices))
To get the value
AcceptedChoices: self.ConvertToInteger(self.AcceptedChoices()),
You need to be checking to see if the Id of a choice is in the AcceptedChoices array. Use the ko.utils array function to help do that:
checked: function() { return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == ChoiceId();
} !== null }
You could put this into a function on your root object:
self.isChoiceAccepted = function(choiceId){
return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == choiceId;
} !== null
};
then call it in your data-bind as:
checked: function() { return $root.isChoiceAccepted(ChoiceId()); }
This isn't tested, I'm not 100% sure that the arrayFirst method returns null if it doesn't find a matching item in the array, so chack that.

Twitter Typeahead 0.9.3 returning json object

I'm attempting to return a json object in my typeahead rather than just a single query. The idea is to pass along additional fields in my autocomplete menus to help filter down the results. Example year, make, model, trim. the json object would contain the filteredBy field id and field value. Example when searching make, the json object will contain the filter field id "year" and value"2013" plus the current field id and it's value.
Currently this works below, but only returns a single value.
return $field.typeahead({
replace: (uri, query) {
return extendURL(uri, "t:input": query);
}
}
This is how I'm trying to do it.
<input id="year" value="2013"/>
<input id="make" filterBy="year" value="ford"/>
<input id="model" filterBy="ford" value="fusion"/>
init = function(spec) {
var $field = $("#" + spec.id),
filterIdArray = [];
if (typeof spec.filterId !== 'undefined') {
filterIdArray = spec.filterId.split(',');
}
return $field.typeahead({
replace: function(uri, query) {
var params = {};
params["t:jsonStringField"] = searchField(spec.id, query);
params["t:jsonStringFilter"] = searchFilter(filterIdArray);
var stringify = JSON.stringify(params);
//I'm trying to pass back this stringified json object.
return extendURL(uri, stringify);
}
}
//The current search input
searchField = function(fieldId, query) {
return {name: fieldId, value: query};
};
//Additional fields used to create where clause within searchField
searchFilter = function(filterIdArray) {
var array = [];
//Field Id's and any fields being filtered by
for (var i = 0; i < filterIdArray.length; i++) {
var value = $("#" + filterIdArray[i]).val(),
name = filterIdArray[i];
array.push({name: name, value: value});
};
return array;
};
I end up getting the following error.
"NetworkError: 500 Internal Server Error - /TapDemo/sell/createlisting.year:autocomplete?category=aircraft&0={&1=%22&2=t&3=:&4=j&5=s&6=o&7=n&8=S&9=t&10=r&11=i&12=n&13=g&14=F&15=i&16=e&17=l&18=d&19=%22&20=:&21={&22=%22&23=n&24=a&25=m&26=e&27=%22&28=:&29=%22&30=y&31=e&32=a&33=r&34=%22&35=,&36=%22&37=v&38=a&39=l&40=u&41=e&42=%22&43=:&44=%22&45=5&46=%22&47=}&48=,&49=%22&50=j&51=s&52=o&53=n&54=S&55=t&56=r&57=i&58=n&59=g&60=F&61=i&62=l&63=t&64=e&65=r&66=%22&67=:&68=[&69=]&70=}"
Does anybody know what I'm doing wrong?
What you need to do is, send these values as json encode and make sure you decode the data as an array, in that way you be able to handle these values.

How to convert jQuery.serialize() data to JSON object?

Is there any better solution to convert a form data that is already serialized by jQuery function serialize(), when the form contains multiple input Array fields. I want to be able to convert the form data in to a JSON object to recreate some other informative tables. So tell me a better way to get the serialize string converted as a JSON object.
<form id='sampleform'>
<input name='MyName' type='text' /> // Raf
<!--array input fields below-->
<input name='friendname[]' type='text' /> // Bily
<input name='fiendemail[]' type='text' /> // bily#someemail.com
<!--duplicated fields below to add more friends -->
<input name='friendname[]' type='text' /> // Andy
<input name='fiendemail[]' type='text' /> // Andy#somwhere.com
<input name='friendname[]' type='text' /> // Adam
<input name='fiendemail[]' type='text' /> // Adam#herenthere.com
</form>
The jquery method applied to get the data
var MyForm = $("#sampleform").serialize();
/** result : MyName=Raf&friendname[]=Billy&fiendemail[]=bily#someemail.com&friendname[]=Andy&fiendemail[]=Andy#somwhere.com&friendname[]=Adam&fiendemail[]=Adam#herenthere.com
*/
how do I make this data in to a JSON object?
which should have the following example JSON data from the above form.
{
"MyName":"raf",
"friendname":[
{"0":"Bily"},
{"1":"Andy"},
{"2":"Adam"}
],
"friendemail":[
{"0":"bily#someemail.com"},
{"1":"Andy#somwhere.com"},
{"2":"Adam#herenthere.com"}
]
}
var formdata = $("#myform").serializeArray();
var data = {};
$(formdata ).each(function(index, obj){
data[obj.name] = obj.value;
});
Simple and fast ;)
I have recently had this exact problem. Initially, we were using jQuery's serializeArray() method, but that does not include form elements that are disabled. We will often disable form elements that are "sync'd" to other sources on the page, but we still need to include the data in our serialized object. So serializeArray() is out. We used the :input selector to get all input elements (both enabled and disabled) in a given container, and then $.map() to create our object.
var inputs = $("#container :input");
var obj = $.map(inputs, function(n, i)
{
var o = {};
o[n.name] = $(n).val();
return o;
});
console.log(obj);
Note that for this to work, each of your inputs will need a name attribute, which will be the name of the property of the resulting object.
That is actually slightly modified from what we used. We needed to create an object that was structured as a .NET IDictionary, so we used this: (I provide it here in case it's useful)
var obj = $.map(inputs, function(n, i)
{
return { Key: n.name, Value: $(n).val() };
});
console.log(obj);
I like both of these solutions, because they are simple uses of the $.map() function, and you have complete control over your selector (so, which elements you end up including in your resulting object). Also, no extra plugin required. Plain old jQuery.
Use the jQuery.serializeJSON plugin.
It converts forms using the same format as what you would find in a Rails params object, which is very standard and well tested.
I'm using this very little jQuery plugin, that I've extended from DocumentCloud:
https://github.com/documentcloud/documentcloud/blob/master/public/javascripts/lib/jquery_extensions.js
It is basically two lines of code, but it requires _.js (Underscore.js), since it is based on a reduce function.
$.fn.extend({
serializeJSON: function(exclude) {
exclude || (exclude = []);
return _.reduce(this.serializeArray(), function(hash, pair) {
pair.value && !(pair.name in exclude) && (hash[pair.name] = pair.value);
return hash;
}, {});
}
});
Extensions:
It doesn't serialize an input value if it's null
It can exclude some inputs by passing an array of input names to the exclude argument i.e. ["password_confirm"]
I think there're a lot of good answer here, and I made my own function based on these answers.
function formToJSON(f) {
var fd = $(f).serializeArray();
var d = {};
$(fd).each(function() {
if (d[this.name] !== undefined){
if (!Array.isArray(d[this.name])) {
d[this.name] = [d[this.name]];
}
d[this.name].push(this.value);
}else{
d[this.name] = this.value;
}
});
return d;
}
//The way to use it :
$('#myForm').submit(function(){
var datas = formToJSON(this);
return false;
});
Well let me explain basically why I prefer this solution...
If you have multiples input with the same name, all values will be stored in an Array but if not, the value will be stored directly as the value of the index in the JSON ... This is where it's different from Danilo Colasso's answer where the JSON returned is only based of array values...
So if you have a Form with a textarea named content and multiples authors, this function will return to you :
{
content : 'This is The Content',
authors :
[
0: 'me',
1: 'you',
2: 'him',
]
}
An equivalent solution to Danilo Colasso's, with the sames pros and cons of .serializeArray() (basically it uses .reduce instead of $.each).
With little effort it allows implementing the extra features in S.C.'s answers without requiring extensions.
$(selector).serializeArray()
.reduce(function(accum, item) {
// This 'if' allows ignoring some values
if (-1 === [ 'ignoreThis', 'andThat' ].indexOf(item.name)) {
// This allows ignoring NULL values
if (item.value !== null) {
accum[item.name] = item.value;
}
}
return accum;
},
{
// By supplying some initial values, we can add defaults
// for, say, disabled form controls.
preFilledName: preFilledValue, // optional
defaultName : defaultValue // optional
}
);
if you can use ES6, you could do
const obj = arr.reduce((acc, {name, value}) => ({...acc, [name]: value}), {})
for a serialized array works very well.
var formdata = $("#myform").serializeArray();
var data = {};
$(formdata ).each(function(index, obj){
if(data[obj.name] === undefined)
data[obj.name] = [];
data[obj.name].push(obj.value);
});
Using underscore & jQuery
var formdata = $("#myform").serializeArray();
var data = {};
_.each(formdata, function(element){
// Return all of the values of the object's properties.
var value = _.values(element);
// name : value
data[value[0]] = value[1];
});
console.log(data); //Example => {name:"alex",lastname:"amador"}
Using the power of reducing function!
$(form).serializeArray().reduce(function (output, value) {
output[value.name] = value.value
return output
}, {})
With all Given Answer there some problem which is...
If input name as array like name[key], but it will generate like this
name:{
key : value
}
For Example :
If i have form like this.
<form>
<input name="name" value="value" >
<input name="name1[key1]" value="value1" >
<input name="name2[key2]" value="value2" >
<input name="name3[key3]" value="value3" >
</form>
Then It will Generate Object like this with all given Answer.
Object {
name : 'value',
name1[key1] : 'value1',
name2[key2] : 'value2',
name3[key3] : 'value3',
}
But it have to Generate like below,anyone want to get like this as below.
Object {
name : 'value',
name1 : {
key1 : 'value1'
},
name2 : {
key2 : 'value2'
},
name3 : {
key2 : 'value2'
}
}
Then Try this below js code.
(function($) {
$.fn.getForm2obj = function() {
var _ = {};
$.map(this.serializeArray(), function(n) {
const keys = n.name.match(/[a-zA-Z0-9_]+|(?=\[\])/g);
if (keys.length > 1) {
let tmp = _;
pop = keys.pop();
for (let i = 0; i < keys.length, j = keys[i]; i++) {
tmp[j] = (!tmp[j] ? (pop == '') ? [] : {} : tmp[j]), tmp = tmp[j];
}
if (pop == '') tmp = (!Array.isArray(tmp) ? [] : tmp), tmp.push(n.value);
else tmp[pop] = n.value;
} else _[keys.pop()] = n.value;
});
return _;
}
console.log($('form').getForm2obj());
$('form input').change(function() {
console.clear();
console.log($('form').getForm2obj());
});
})(jQuery);
console.log($('form').getForm2obj());
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<form>
<input name="name" value="value">
<input name="name1[key1]" value="value1">
<input name="name2[key2]" value="value2">
<input name="name3[key3]" value="value3">
<input type="checkbox" name="name4[]" value="1" checked="checked">
<input type="checkbox" name="name4[]" value="2">
<input type="checkbox" name="name4[]" value="3">
</form>
if you are using ajax requests then no need to make it json-object only $('#sampleform').serialize() works excellently or if you have another purpose here is my solution:
var formserializeArray = $("#sampleform").serializeArray();
var jsonObj = {};
jQuery.map(formserializeArray , function (n, i) {
jsonObj[n.name] = n.value;
});
Use JSON.stringify() and serializeArray():
console.log(JSON.stringify($('#sampleform').serializeArray()));

Categories