I have an array of objects, an example of a few of them being
[
{
"lang_code": "eng",
"site_language": "1",
"name": "English"
},
{
"lang_code": "afr",
"site_language": "1",
"name": "Afrikaans"
},
{
"lang_code": "ale",
"site_language": "0",
"name": "Aleut"
},
]
I want to be able to search the whole array for a specific lang_code, let's say I use eng. I want to search the whole array for eng. If it's there, I want it to return English, if not, I want it to return invalid. Any ideas on this?
A generic and more flexible version of the findById function above:
// array = [{key:value},{key:value}]
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
var array = your array;
var result_obj = objectFindByKey(array, 'lang_code', 'eng');
Click here for demo
You could use filter:
function findLang(arr, code) {
var filtered = arr.filter(function (el) {
return el.lang_code === code;
});
// filter returns an array of objects that match the criteria
// if the array is not empty return the language,
// otherwise return 'invalid'
return filtered.length > 0 ? filtered[0].name : 'invalid';
}
findLang(arr, 'eng'); // English
DEMO
If you wanted to add map into the mix instead of using that ternary operation (but which would most likely be slower and doesn't really provide any additional benefit):
function findLang(arr, code) {
return arr.filter(function (el) {
return el.lang_code === code;
}).map(function (el) {
return el.name;
})[0] || 'invalid';
}
DEMO
How about a for loop. Something like:
function find_lang(input_arr, lang_code) {
for(var i = 0; i < input_arr.length; i++) {
var o = input_arr[i];
if( o.lang_code === lang_code ) {
return o.name;
}
}
return "invalid";
}
Underscore.js has some useful helpers for this kind of thing: http://underscorejs.org/
E.g Find:
Looks through each value in the list, returning the first one that
passes a truth test (predicate), or undefined if no value passes the
test. The function returns as soon as it finds an acceptable element,
and doesn't traverse the entire list.
var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> 2
Where:
Looks through each value in the list, returning an array of all the
values that contain all of the key-value pairs listed in properties.
_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
{title: "The Tempest", author: "Shakespeare", year: 1611}]
You could do it this way:
// configure your parameters
function checkArr(){
for (var i = 0; i < x.length; i++){
if (x[i].lang_code == "eng")
return "English";
}
return "invalid";
}
var x = [
{
"lang_code": "eng",
"site_language": "1",
"name": "English"
},
{
"lang_code": "afr",
"site_language": "1",
"name": "Afrikaans"
},
{
"lang_code": "ale",
"site_language": "0",
"name": "Aleut"
}
];
Related
I want to summarize an array of objects and return the number of object occurrences in another array of objects. What is the best way to do this?
From this
var arrayOfSongs = [
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];
To this
var newArrayOfSongs = [
{"title": "Blue", "playCount": 3 },
{"title": "Green", "playCount": 1}
]
I have tried
arrayOfSongs.reduce(function(acc, cv) {
acc[cv.title] = (acc[cv.title] || 0) + 1;
return acc;
}, {});
}
But it returns an object:
{ "Blue": 3, "Green": 1};
You should pass the initial argument to the reduce function as an array instead of object and filter array for the existing value as below,
Working snippet:
var arrayOfSongs = [
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];
var newArrayOfSongs = arrayOfSongs.reduce(function(acc, cv) {
var arr = acc.filter(function(obj) {
return obj.title === cv.title;
});
if(arr.length === 0) {
acc.push({title: cv.title, playCount: 1});
} else {
arr[0].playCount += 1;
}
return acc;
}, []);
console.log(newArrayOfSongs);
To build on what you already have done, the next step is to "convert" the object to an array
var arrayOfSongs = [
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];
var obj = arrayOfSongs.reduce(function(acc, cv) {
acc[cv.title] = (acc[cv.title] || 0) + 1;
return acc;
}, {});
// *** added code starts here ***
var newArrayOfSongs = Object.keys(obj).map(function(title) {
return {
title: title,
playCount:obj[title]
};
});
console.log(newArrayOfSongs);
I recommend doing this in two stages. First, chunk the array by title, then map the chunks into the output you want. This will really help you in future changes. Doing this all in one pass is highly complex and will increase the chance of messing up in the future.
var arrayOfSongs = [
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Blue","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"},
{"title":"Green","duration":161.71,"audioUrl":"/assets/music/blue","playing":false,"playedAt":"2016-12-21T22:58:55.203Z"}
];
function chunkByAttribute(arr, attr) {
return arr.reduce(function(acc, e) {
acc[e[attr]] = acc[e[attr]] || [];
acc[e[attr]].push(e);
return acc;
}, {});
}
var songsByTitle = chunkByAttribute(arrayOfSongs, 'title');
var formattedOutput = Object.keys(songsByTitle).map(function (title) {
return {
title: title,
playCount: songsByTitle[title].length
};
});
There, now everything is named according to what it does, everything does just one thing, and is a bit easier to follow.
https://jsfiddle.net/93e35wcq/
I used a set object to get the unique track titles, then used Array.map to splice those and return a song object that contains play count inside the track title.
The Data:
var arrayOfSongs = [{
"title": "Blue",
"duration": 161.71,
"audioUrl": "/assets/music/blue",
"playing": false,
"playedAt": "2016-12-21T22:58:55.203Z"
}, {
"title": "Blue",
"duration": 161.71,
"audioUrl": "/assets/music/blue",
"playing": false,
"playedAt": "2016-12-21T22:58:55.203Z"
}, {
"title": "Blue",
"duration": 161.71,
"audioUrl": "/assets/music/blue",
"playing": false,
"playedAt": "2016-12-21T22:58:55.203Z"
}, {
"title": "Green",
"duration": 161.71,
"audioUrl": "/assets/music/blue",
"playing": false,
"playedAt": "2016-12-21T22:58:55.203Z"
}];
The Function:
function getPlayCount(arrayOfSongs) {
let songObj = {};
let SongSet = new Set();
arrayOfSongs.map(obj => (SongSet.has(obj.title)) ? true : SongSet.add(obj.title));
for (let songTitle of SongSet.values()) {
songObj[songTitle] = {
playCount: 0
};
arrayOfSongs.map(obj => (obj.title === songTitle) ? songObj[songTitle].playCount++ : false)
}
return songObj;
}
console.log(getPlayCount(arrayOfSongs));
Which isn't exactly what you wanted formatting wise, but if you're married to it, this will do the trick:
function getPlayCount(arrayOfSongs) {
let songObj = {};
let SongSet = new Set();
arrayOfSongs.map(obj => (SongSet.has(obj.title)) ? true : SongSet.add(obj.title));
for (let songTitle of SongSet.values()) {
songObj[songTitle] = 0;
arrayOfSongs.map(obj => (obj.title === songTitle) ? songObj[songTitle]++ : false)
}
return songObj;
}
console.log(getPlayCount(arrayOfSongs));
https://jsfiddle.net/93e35wcq/1/
I am Having the Array of objects. Like this
var result=[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"},
{"batchId":455, "licenseId":asfd236645 },
{"batchId":678, "name":"aaa"}]
i want to have the array which is contains all the three properties. the Output should be like this.
[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"}]
can anybody Help me on this
This is simple with the array .filter() method:
var result=[
{"batchId":123, "licenseId":"2345ef34", "name":"xxx"},
{"batchId":345, "licenseId":"2345sdf334", "name":"www"},
{"batchId":145, "licenseId":"234sdf5666", "name":"eee"},
{"batchId":455, "licenseId":"asfd236645" },
{"batchId":678, "name":"aaa"}
];
var filtered = result.filter(function(v) {
return "batchId" in v && "licenseId" in v && "name" in v;
});
console.log(filtered);
The function you pass to .filter() is called for each element in the array. Each element for which you return a truthy value will be included in the resulting array.
In the code above I simply test if all three of those specific properties are present, although there are other tests you could use that would get the same result for that data:
var result=[ {"batchId":123, "licenseId":"2345ef34", "name":"xxx"}, {"batchId":345, "licenseId":"2345sdf334", "name":"www"}, {"batchId":145, "licenseId":"234sdf5666", "name":"eee"}, {"batchId":455, "licenseId":"asfd236645" }, {"batchId":678, "name":"aaa"} ];
var filtered = result.filter(function(v) {
return Object.keys(v).length === 3;
});
console.log(filtered);
Note that you need to put your licenseId values in quotes, because they seem to be string values.
var result = [{
"batchId": 123,
"licenseId": '2345ef34',
"name": "xxx"
}, {
"batchId": 345,
"licenseId": '2345sdf334',
"name": "www"
}, {
"batchId": 145,
"licenseId": '234sdf5666',
"name": "eee"
}, {
"batchId": 455,
"licenseId": 'asfd236645'
}, {
"batchId": 678,
"name": "aaa"
}];
function hasProperties(object) {
return object.hasOwnProperty('batchId') && object.hasOwnProperty('licenseId') && object.hasOwnProperty('name')
}
result.filter(e => hasProperties(e));
We have MongoDB docs that look like this:
var JavascriptObject = {
DbDocs : [
{
_id : "1",
{..more values..}
},
{
_id : "2",
{..more values..}
},
{
_id : "3",
{..more values..}
}
]
}
Based on certain values in the JavascriptObject, we order an array of the _id from the documents, and the result is this:
var OrderedArray = [ 2, 1, 3 ];
Right now, we're rebuilding the entire JavascriptObject by matching the _id in the OrderedArray with the _id in DbDocs:
var JavascriptObjectToRebuild = [];
var DbDocuments = JavascriptObject.DbDocs;
var DocumentCount = 0;
for (var OrderedNumber in OrderedArray) {
for (var Document in DbDocuments) {
if ( DbDocuments[Document]._id === OrderedArray[OrderedNumber] ) {
JavascriptObjectToRebuild[DocumentCount] = {}; // new Document Object
JavascriptObjectToRebuild[DocumentCount]._id = DbDocuments[Document]._id;
JavascriptObjectToRebuild[DocumentCount]...more values = DbDocuments[Document]...more values;
DocumentCount++; // increment
}
}
}
var SortedJavascriptObject = { DbDocs: [] }; // format for client-side templating
for (var Document in JSONToRebuild) {
SortedJavascriptObject.DbDocs.push(JavascriptObjectToRebuild[Document]);
}
Is there a faster more efficient way to sort the JavascriptObject based on this OrderedArray?
See update below if it's impossible to sort directly and you have to use OrderedArray instead.
If you can apply your criteria within the callback of the Array#sort function (e.g., if you can do it by comparing two entries in the array to one another), you can simply sort JSON.DbDocs directly.
Here's an example that sorts based on the numeric value of _id; naturally you'd replace that with your logic comparing objects.
Also note I've changed the name of the top-level variable (JSON is kinda in use, and in any case, it's not JSON):
var Obj = {
DbDocs : [
{
_id : "2",
more: "two"
},
{
_id : "1",
more: "one"
},
{
_id : "3",
more: "three"
}
]
};
Obj.DbDocs.sort(function(a, b) {
return +a._id - +b._id; // Replace with your logic comparing a and b
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If it's impossible to sort directly and you have to work from OrderedArray, then it's still possible with sort, but it's less elegant: You use Array#indexOf to find out where each entry in the array should be:
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
(The + converts the IDs from strings to numbers, since OrderedArray contains numbers in your question, but the ID values are strings.)
Live Example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If there are going to be lots of entries in OrderedArray, you might want to make a lookup object first, to avoid lots of indexOf calls (which are costly: (georg did that in an answer, but he's since deleted it for some reason)
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
(We don't need to convert the IDs to numbers because property names are always strings, so we've converted the numbers to strings when building the map.)
Live Example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
As I understood, you want to get result like so,
[{"_id":"2"}, {"_id":"1"}, {"_id":"3"}]
so you can do it with one forEach and indexOf, like so
var JSONDADA = {
DbDocs : [{_id : "1",}, {_id : "2"}, {_id : "3"}]
};
var DbDocuments = JSONDADA.DbDocs;
var OrderedArray = [ 2, 1, 3 ];
var result = [];
DbDocuments.forEach(function (el) {
var position = OrderedArray.indexOf(+el._id);
if (position >= 0) {
result[position] = el;
}
});
console.log(JSON.stringify(result));
303 see other
......
Convert OrderedNumber into a hash _id => position:
sorter = {}
OrderedNumber.forEach(function(_id, pos) {
sorter[_id] = pos
})
and then sort the target array by comparing id's positions:
DbDocuments.sort(function(a, b) {
return sorter[a._id] - sorter[b._id];
})
I would like to know what is the faster way to retrieve an object by its id, keeping in consideration that the structure where it is stored could be different.
Does any native function in JS for this operation?
Example 1
var source = {
"page": [{
"id": "1",
"site": "",
"items": [{
"id": "2"
}, {
"id": "3",
"anotherItem": [{
"id": "4"
}, {
"id": "5"
}]
}]
}, {
"id": "6"
}]
};
Example 2
Structure could be completely different, the script should be always able to get the object containing id.
{
"smtElse": {
"id": "1"
}
}
No, there is no native function.
The fastest way is the same as the only way which is to iterate over the object, probably recursively, looking for IDs and returning what you want.
I solved using the following script, as it seems no native functionality is available.
var data = {
item: [
{
itemNested: [
{
itemNested2: [{
id: "2"
}
]
}
]
}
]
};
function findById(obj, id) {
var result;
for (var p in obj) {
if (obj.id == id) {
return obj;
} else {
if (typeof obj[p] === 'object') {
result = findById(obj[p], id);
if (result) {
return result;
}
}
}
}
return result;
}
var result = findById(data, "2");
console.log(result);
you can try this
<script type="text/javascript">
var re = /"id":\s+"(.*?)"/;
var sourcestring = "source string to match with pattern";
var results = [];
var i = 0;
for (var matches = re.exec(sourcestring); matches != null; matches = re.exec(sourcestring)) {
results[i] = matches;
for (var j=0; j<matches.length; j++) {
alert("results["+i+"]["+j+"] = " + results[i][j]);
}
i++;
}
Here is what I want to do:
I have a tree (javascript object-literal structure) with multiple levels.
I have a value of a particular key of this object.
I want to search for this exact key-value pair in the structure and return the value of another key as an output.
For clarity following is my object literal:
{
"nodeId": 1081,
"appId": 150,
"displayText": "Welcome here",
"Nodes": [
{
"nodeId": 2000,
"appId": 150,
"displayText": "Buy",
"parentNodeId": 1081,
"Nodes": [
{
"nodeId": 2003,
"appId": 150,
"displayText": "tCars",
"parentNodeId": 2000,
"Nodes": [
{
"nodeId": 2006,
"appId": 150,
"displayText": "Diesel",
"parentNodeId": 2003,
"Nodes": [
{
"nodeId": 2008,
"appId": 150,
"displayText": "Price", //This is what I want as return value.
"parentNodeId": 2006,
"Nodes": [],
"nodeCode": "RN_1_1_2_1_3_2_4_1",
"parentCode": "RN_1_1_2_1_3_2",
"jumpToNode": "RN_1_1" //here is the value that I have with me.
}
],
"nodeCode": "RN_1_1_2_1_3_2",
"parentCode": "RN_1_1_2_1"
}
],
"concatWithHeader": false,
"nodeCode": "RN_1_1_2_1",
"parentCode": "RN_1_1"
}
],
"nodeCode": "RN_1_1",
"parentCode": "RN"
}
],
"nodeCode": "RN",
"parentCode": "ROOT_NODE"
}
2. Value that I have with me is "RN_1_1" against jumpToNode
3. I want to search in this object literal and get the value of the key displayText
I searched and tried few things for this but couldnt get the logic to iterate over the inner Nodes objects.
Method I wrote so far:
function getObjects(tree){
var searchkey="RN_1_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if (tree.hasOwnProperty(nodeCode)) {
var obj = tree[nodeCode];
if(obj == searchkey){
returnText = tree[displayText]; //gives me the return text
break;
}
else{
//here I should iterate over the inner `Nodes` and get the required value.
}
}
}
Please help.
Thanks.
I think you can do something like this which works recursively:
function findProperty(obj, prop, val, propToFetch) {
var answer;
if (obj.hasOwnProperty(prop) && obj[prop] === val) {
return obj[propToFetch];
}
for (var i = 0, len = obj.Nodes.length; i < len; i++) {
answer = findProperty(obj.Nodes[i], prop, val, propToFetch);
if (answer !== null) {return answer;}
}
return null;
}
var result = findProperty(data, "jumpToNode", "RN_1_1", "displayText");
Working demo here: http://jsfiddle.net/jfriend00/EjC5V/
Accordingly to your JSON object you can use this way:
var searchKey="RN_1_1",
displayText = "displayText",
nodeCode = "nodeCode",
returnText,
treeSearch = function (obj, searchKey) {
if (obj[nodeCode] === searchKey) {
returnText = obj[displayText];
} else {
if (obj['Nodes'][0]) {
treeSearch(obj['Nodes'][0], searchKey);
} else {
returnText = null
}
}
};
treeSearch(JSONdata, 'RN_1_1_2_1_3_2');
I have flattened the array using the nodeId to be easier to search through it.
After you flatten the array you can filter it as you wish(i suggest using underscorejs.org)
Here is the live example. The result is displayed in the console.
function flattenNodes (obj, newArr) {
if (obj && obj.Nodes) {
var nodes = obj.Nodes;
delete(obj.Nodes);
newArr[obj.nodeId] = obj;
return flattenNodes(nodes.pop(), newArr);
} else {
return newArr;
}
};
var flattenArr = flattenNodes(arr, new Array());
function findInJumpToNode(find) {
for(key in flattenArr) {
if (flattenArr[key] && flattenArr[key]['jumpToNode']) {
if (flattenArr[key]['jumpToNode'] == find) {
return flattenArr[key];
}
}
}
}
var found = findInJumpToNode('RN_1_1');
console.log(found);
You can use recursion to handle your case.
Check out this sample on jsFiddle.
var nodes= [getNodes()];
alert(getObjects(nodes));
function getObjects(tree){
var searchkey="RN_1_1_2_1_3_2_4_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if(tree.length > 0)
{
if(tree[0]["nodeCode"] === searchkey)
{
return tree[0][displayText];
}
if(typeof tree[0]["Nodes"] === "undefined")
{
return;
}
return getObjects(tree[0]["Nodes"]);
}
}