I have a JavaScript object like this:
[{
name : "soccer",
elems : [
{name : "FC Barcelona"},
{name : "Liverpool FC"}
]
},
{
name : "basketball",
elems : [
{name : "Dallas Mavericks"}
]
}]
Now I want to search on this JavaScript object in the browser. The search for "FC" should give me something like this:
[
{name : "FC Barcelona"},
{name : "Liverpool FC"}
]
How to do this fast? Are there any JavaScript libs for this?
You might like using jLinq (personal project)
http://hugoware.net:4000/Projects/jLinq
Works like LINQ but for JSON and it allows you to extend it and modify it however you want to. There is already a bunch of prebuilt methods to check values and ranges.
Seeing as though the only helpful answers have been referencing third party libraries - here's your native javascript solution. For anyone that only wants a few lines of code rather than a stack:
The function:
Array.prototype.findValue = function(name, value){
var array = map(this, function(v,i){
var haystack = v[name];
var needle = new RegExp(value);
// check for string in haystack
// return the matched item if true, or null otherwise
return needle.test(haystack) ? v : null;
});
return array;
}
A native .map() function:
map = function(array, mapFunction) {
var newArray = new Array(array.length);
for(var i = 0; i < array.length; i++) {
newArray[i] = mapFunction(array[i]);
}
return newArray;
}
Your object:
(skimmed from your posted abject):
myObject = {
name : "soccer",
elems : [
{name : "FC Barcelona"},
{name : "Liverpool FC"}
]
},
{
name : "basketball",
elems : [
{name : "Dallas Mavericks"}
]
}
For usage:
(This will search your myObject.elems array for a 'name' matching 'FC')
var matched = myObject.elems.findValue('name', 'FC');
console.log(matched);
The result - check your console:
[Object, Object, findValue: function]
0: Object
name: "FC Barcelona"
__proto__: Object
1: Object
name: "Liverpool FC"
__proto__: Object
length: 2
__proto__: Array[0]
Try jOrder. http://github.com/danstocker/jorder
It's optimized for fast O(logn) search and sorting on large (thousands of rows) tables in JS.
As opposed to array iteration, which most of the answers here are based on, jOrder uses indexes to filter data. Just to give you an idea, free-text search on a 1000-row table completes about 100 times faster than iteration. The bigger the table, the better ratio you get.
However jOrder can't process the format of your sample data. But if you re-format it like this:
var teams =
[
{ sport : "soccer", team: "FC Barcelona" },
{ sport : "soccer", team: "Liverpool FC" },
{ sport : "basketball", team : "Dallas Mavericks"}
]
You can get the desired results by first setting up a jOrder table:
var table = jOrder(teams)
.index('teams', ['team'], { grouped: true, ordered: true, type: jOrder.text });
And then running a search on it:
var hits = table.where([{ team: 'FC' }], { mode: jOrder.startof });
And you'll get exactly the two rows you needed. That's it.
The straightforward way to do this is simply to iterate over every property of the object and apply a test function to them (in this case, value.contains("FC")).
If you want it to go faster, you'd either need to implement some kind of caching (which could be eagerly populated in the background ahead of any queries), or perhaps precalculate the result of various popular test functions.
You could do this with regular expressions performed against a serialized JSON string:
var jsonString = "[{ name : \"soccer\", elems : [ {name : \"FC Barcelona\"}"
+", {name : \"Liverpool FC\"}]},{name : \"basketball\",elems : ["
+"{name : \"Dallas Mavericks\"} ]}]";
var pattern = /\s*([\w\d_]+)\s*:\s*((\"[^\"]*(your pattern here)[^\"]*\")|(\'[^\']*(your pattern here)[^\']*\'))\s*/g;
var foundItems = [];
var match;
while(match = pattern.exec(jsonString)){
foundItems.push(match[0]);
}
var foundJSON = "[{" + foundItems.join("}, {") + "}]";
var foundArray = eval(foundJSON);
I haven't tested the loop part of this, but the Regex seems to be working well for me with simple tests in firebug.
In regards to AngularJS, you can do this:
var item = "scope-ng-model";
(angular.element('form[name="myForm"]').scope())[item] = newVal;
Related
I am working on a chrome plugin that fetches data. But now i have been running into a problem, I have been asked to put together a nested array with all the data I have retrieved but I have no clue on how to pull this off.
What i want to create:
var messagedata [{
time: messageTime,
Date: messageDate,
Text: messageText
{
time: messageTime,
Date: messageDate,
Text: messageText
}
}];
Note that I know how to create the above when I have the variables. That is not the problem. But in this case i do not know how to declare the variables for each message from the array that is generated.
What i need is a nested array for each message that is in the HTML. So the above example displays 2 arrays but it could be 54 for example.
Code i use to generate normal array:
adiv.innerHTML = cleanupDocString;
trs = adiv.querySelectorAll('tr[bgcolor="#FFFFFF"]');
trs.forEach(function(tr) {
var d = [];
tr.querySelectorAll("td")
.forEach(function(td) {
var img = td.querySelector("img"),
src = img && img.attributes.getNamedItem("src").value;
d.push(src || td.textContent);
});
msgs.push(d);
});
The code above puts this out in console (this example has 2 messages inside it, there are also arrays with 54 messages):
0:Array(6)
0:"2017-08-31T00:00:00"
1:"13:22"
2:"MessageType"
3:”ClientName"
4:"Subject "
5:"messageText"
length:6
proto:Array(0)
1:Array(6)
0:"2017-08-31T00:00:00"
1:"13:21"
2:" MessageType "
3: "ClientName"
4:" Subject "
5:" messageText "
lenth:6
proto:Array(0)
To make the question easier:
I need to know how i can put the data into a variable that i fetch from the array above. I just don't know how to do it so its dynamic.
What i tried:
var messageDate = msgs[0][0];
var messageTime = msgs[0][1];
var messageType = msgs[0][2];
var messageClient = msgs[0][3];
var messageSubject = msgs[0][4];
var messageText = msgs[0][5];
The above code works but only fetches the first message. I need all the messages that are on the page that is provided. I tried using a ID in the first [] but that also didn't give me the desired result.
Thanks for your help and patience in advance.
Output and code has been slightly edited so it hides personal information
i am assuming msgs is arrray of arrays and the order of properties is guaranteed
var mappedArray = msgs.map((msg)=> {
return {
messageDate : msg[0];
messageTime : msg[1];
messageType : msg[2];
messageClient : msg[3];
messageSubject : msg[4];
messageText :msg[5];
}
})
Edit1
you can use arrayconcat
var mergedArray = mappedArray.concat(otherArray);
To transform the multidimensional array to an array of objects with the help of Array.prototype.map and a simple helper dictionary which defines the index => property mapping.
var messages = [
[
"2017-08-31T00:00:00",
"13:22",
"MessageType",
"ClientName",
"Subject",
"messageText",
"unwanted value"
],
[
"2017-08-31T00:00:00",
"13:22",
"MessageType",
"ClientName",
"Subject",
"messageText",
"unwanted value"
],
[
"2017-08-31T00:00:00",
"13:22",
"MessageType",
"ClientName",
"Subject",
"messageText",
"unwanted value"
]
];
var mappingDef = {
0: 'messageDate',
1: 'messageTime',
2: 'messageType',
3: 'messageClient',
4: 'messageSubject',
5: 'messageText'
};
function transformMessages(messages, mappingDef) {
return messages.map(function(message) {
var obj = {};
for(var index in mappingDef) {
if(mappingDef.hasOwnProperty(index)) {
obj[mappingDef[index]] = message[index];
}
}
return obj;
});
}
console.log(transformMessages(messages, mappingDef));
After several test and search, I can't find a way to navigate after parsing a JSON; here is the post-parsing result :
Object {documentation: "https://geocoder.opencagedata.com/api", licenses: Array[2], rate: Object, results: Array[1], status: Object…}
documentation
:
"https://geocoder.opencagedata.com/api"
licenses
:
Array[2]
rate
:
Object
results
:
Array[1]
0
:
Object
annotations
:
Object
components
:
Object
building
:
"C"
city
:
"Bordeaux"
country
:
"France"
country_code
:
"fr"
county
:
"Bordeaux"
postcode
:
"33000"
road
:
"Quai de Bacalan"
state
:
"Aquitaine"
suburb
:
"Bordeaux Maritime"
For example I can get the value of the response with the following code :
var locname = response.status.code;
But in the case there is a int as Object, like this :
var locname = response.results.0.formatted;
I have the following error :
Uncaught SyntaxError: Unexpected number
I try to escape the character, putting quote, etc but I couldn't find any solution.
in javascript, an object is also accessible as array, so for example, you have an object like this:
var obj = {name: 'Hans Yulian', age: 21, message: 'Handsome'};
obj[0] = 'This is an number index';
obj['message'] = 'Too Handsome';
all the key is accepted as long as they aren't something that contain special characters (-+=! etc) and not started with number. in any case you have the field that don't satisfy this exception, then you have to access it in array-way.
you can assign the value the same way as array, and also to get the content of that field.
in case you need to access something like
var locname = response.results.0.formatted;
Then the thing that you need is
var locname = response.results[0].formatted;
you can try make a html file with this content
<script>
var obj = {name: 'Hans Yulian', age: 21, message: 'Handsome'};
obj[0] = 'This is an number index';
obj['message'] = 'Too Handsome';
obj[1] = {};
obj[1].name = 'Handsome';
obj.handsome = [];
obj.handsome[0] = {};
obj.handsome[0].hansyulian = 'So Handsome';
console.log(obj);
console.log(obj[1].name);
console.log(obj.handsome[0].hansyulian);
</script>
and try see the console(right click, inspect element, select console for google chrome) to understand what happens there
Since results is an array you must use the following syntax:
var locname = response.results[0].formatted;
Instead of
var locname = response.results.0.formatted;
Question 1
Assume I am getting a JSON object scope.tagSet, which is in the following format.
{ Tags : [
{"TagID" : "ID1" , "TagName" : "Name1"},
{"TagID" : "ID2" , "TagName" : "Name2"},
{"TagID" : "ID3" , "TagName" : "Name3"}
]
}
Inside an angular directive I am making an array of all the values in TagName as follows.
for(var i= 0; i < scope.tagSet.Tags.length; i++){
scope.tagNames[i] = scope.tagSet.Tags[i].TagName;
}
Is looping through and assigning each the only option here? Or is there any other way which is more efficient.
Question 2
Say that I've got the array tagNames[] My next job is to perform a search to check if a given variable varString exists in the array tagNames[] and return true or false accordingly.
I know that scope.tagNames.indexOf(varString) will return -1 when there are no matches,
but is it the angular way? Are these approaches considered good practice?
What you can do is make a lookuplist. In javascript you can use the object:
function arrayToLookup(a, key) {
var o = {};
for (var i = a.length; i--;) {
o[a[i][key]] = true;
}
return o;
}
Use it like this
var lookupList = arrayToLookup(scope.tagSet.Tags, "TagName");
now you can check if it is available by doing this:
lookupList["Name1"];//returns true
Why are you so keen to do it the angular way? What if you one day decide to dump angular? That's more likely than keeping angular but dumping JS. This is the JS way:
scope.tagNames = scope.tagSet.Tags.map( function (i) {return i.TagName;} )
Then use indexOf as you suggested at the outset.
You could use the angular-filtermodule to check if your array contains the given item:
$scope.items = [
{"TagID" : "ID1" , "TagName" : "Name1"},
{"TagID" : "ID2" , "TagName" : "Name2"},
{"TagID" : "ID3" , "TagName" : "Name3"}
]
// returns true
$scope.Name1 = $filter('contains')($scope.items,'TagName == "Name1"');
// returns false
$scope.Name4 = $filter('contains')($scope.items,'TagName == "Name4"');
Here is a working plunker
Use this
var arrayNew=[];
angular.forEach(scope.tagSet.Tags, function(value, key) {
arrayNew[i] = value.TagName;
});
scope.tagNames=arrayNew;
I am building a file management system for the web right now.
But I have some problems with javascript array's.
In the system there is an opportunity to add labels to file's.
In javascript I want to have the ID and the value's of the labels with the fileId in 1 array.(as below).
I also want the FileId and the LabelId not as the index of the array's. Because the FileId and labelId can be a realy high number. And then I have an array full of undefined items.
Here an example of how I would like to have it:
array[FileId][labelId,labelValue]
If you have an solution please help me.
Thanks.
You can form structure like this:
arr = [{FieldId:fid_value, Labels:[{labelId:lid_value, labelValue:label_text}]}]
Basically, an array with objects. Each object contains two fields: field id and labels.
Labels is an array with objects also. Each object has label id and label value property.
Code to create new items might be like this:
arr = array();
fieldObj = {FieldId:fid_value, Labels:[]};
fieldObj.Labels.push({labelId:lid_value, labelValue:label_text});
fieldObj.Labels.push({labelId:lid_value, labelValue:label_text});
fieldObj.Labels.push({labelId:lid_value, labelValue:label_text});
...
arr.push(fieldObj);
I'm not entirely sure what you're asking but array within array is possible...
a = []
a.push('a')
Result:
["a"]
a.push(['hello','world'])
Result:
["a",
Array[2]
0: "hello"
1: "world"
]
It sounds like you want objects instead of arrays:
var obj = {};
obj["fieldName"] = {label: "labelname", labelId: 1234};
Then you can access this data as:
obj["fieldName"].label
You could also use an object
var data = {};
data["item1"] = { "labelId" : "foo1", "labelValue" : "bar1" };
data["item2"] = { "labelId" : "foo2", "labelValue" : "bar2" };
console.log(data.item1.labelId);
There are plenty of ways you can strcture the object, it is normally better to use an object than to remember that index 0 is the id and that index 1 is a value.
Use should use objects as well as arrays:
var root = [{
id: '12345',
metadata: {
label: 'foo',
},
type: 'folder',
name: 'Folder Name',
children: [...]
}
];
Now, you can iterate through the folders and files in your root:
for (var i = 0; i < root.length; i++) {
var item = root[i];
console.log(item.type, item.name, item.id);
}
I have a mongodb document with the following structure
> db.user.find().limit(1);
{ "_id" : "1", "foo" : { "bars" : [
{
"name" : "bar1"
},
{
"name" : "bar2"
},
], ... }, ... }
I want to add a new property to each bar. I've got my script iterating over the bars array, but I can't get the new property in there, how can I do this?
var users = db.user.find({"foo.bars":{$exists:true}});
users.forEach(function(user) {
user.foo.bars.forEach(function(bar)
{
printjson(bar);
//how can I specify the current 'bar' in this update?
//db.experience.update({_id: user._id}, {$set: {"what goes here?" : "newbarValue"}});
});
});
So says the preacher man:
var users = db.user.find({"foo.bars":{$exists:true}});
users.forEach(function(user) {
var id = user._id;
var foo_bars = user.foo.bars;
var new_foo_bars = [];
for(var i = 0; i < foo_bars.length; i++) {
var foo_bar = foo_bars[i];
foo_bar['newkey'] = 'newvalue';
new_foo_bars.push(foo_bar);
}
db.user.update({"_id":id}, {$set:{"foo.bars":new_foo_bars}});
});
I have noticed that you are scrolling through the array on the client side there in JS.
If you were to form a new "bars" array from the old one then push it in as a whole new value this would mean you only do one DB call and the code is quite elegant.
If MongoDB does not support it normally it is better to just do the work on the client side.
You have to update each element of the nested document individually. Using the positional operator '$' will not work since it will only work on the first match (as documented).