loop array of objects and add obj for missing entries - javascript

I am doing a particular task but it isnt dynamic. Below i have a eventList and a response from API. Based on the eventList, i have to rearrange the response and sort it.
For eg. below, i have eventList XM1, XM2, XM3, So i have to rearrange response, in such a way that eventTitle with XM1 becomes first element, eventTitle with XM2 is second element and XM3 is third element. And this will repeat till the last element of response is reached. In Below example, once id 1, 3, 2 are pushed sequentially object id 4 is left. But id with object 4 has eventTitle XM2. That means XM1 has to be filled with empty object and XM3 has to be filled with another empty object.
let eventList = [ "XM1", "XM2", "XM3" ];
let response = [
{ "id": 1, "eventTitle": "XM1" },
{ "id": 2, "eventTitle": "XM3" },
{ "id": 3, "eventTitle": "XM2" },
{ "id": 4, "eventTitle": "XM2" },
]
The result of this sequentially placing elements and filling the gaps with id=0 is shown below.
let sortResponse = [
{ "id": 1, "eventTitle": "XM1" },
{ "id": 2, "eventTitle": "XM2" },
{ "id": 3, "eventTitle": "XM3" },
{ "id": 0, "eventTitle": "XM1" },
{ "id": 4, "eventTitle": "XM2" },
{ "id": 0, "eventTitle": "XM3" },
]
Here is the code i use to sequentially sort elements and add empty objects in the output. But this is not dynamic. i always knew that my eventList will be 3 elements. but i want to make it dynamic so that even if my eventList is 10 elements, i should be able to sort and fill missing objects in it. Can someone please let me know how to achieve this dynamically
let sortResponse = []
if (eventList.length === 3 && response.length > 0) {
let fil1 = response.filter(function (el) {
return el.eventTitle === eventList[0];
});
let fil2 = response.filter(function (el) {
return el.eventTitle === eventList[1];
});
let fil3 = response.filter(function (el) {
return el.eventTitle === eventList[2];
});
let obj = { id: 0, eventTitle: "" };
let obj1 = { id: 0, eventTitle: "" };
//check if fil1 has most elements and use it to iterate through each fil1 and push fil2 and fil3
if (fil1.length >= fil2.length && fil1.length >= fil3.length) {
for (let j = 0; j < fil1.length; j++) {
sortResponse.push(fil1[j]);
if (!fil2[j]) {
obj.eventTitle = eventList[1];
}
sortResponse.push(fil2[j] ? fil2[j] : obj);
if (!fil3[j]) {
obj1.eventTitle = eventList[2];
}
sortResponse.push(fil3[j] ? fil3[j] : obj1);
}
}
//check if fil2 has most elements and use it to iterate through each fil2 and push fil1 and fil3
else if (fil2.length >= fil1.length && fil2.length >= fil3.length) {
for (let j = 0; j < fil2.length; j++) {
if (!fil1[j]) {
obj.eventTitle = eventList[0];
}
sortResponse.push(fil1[j] ? fil1[j] : obj);
sortResponse.push(fil2[j]);
if (!fil3[j]) {
obj1.eventTitle = eventList[2];
}
sortResponse.push(fil3[j] ? fil3[j] : obj1);
}
}
//check if fil3 has most elements and use it to iterate through each fil3 and push fil1 and fil2
else if (fil3.length >= fil1.length && fil3.length >= fil2.length) {
for (let j = 0; j < fil3.length; j++) {
if (!fil1[j]) {
obj.eventTitle = eventList[0];
}
sortResponse.push(fil1[j] ? fil1[j] : obj);
if (!fil2[j]) {
obj1.eventTitle = eventList[1];
}
sortResponse.push(fil2[j] ? fil2[j] : obj1);
sortResponse.push(fil3[j]);
}
}
}

This isn't particularly optimized since we're "finding" the next element over and over again in our loop, but it's pretty dynamic so it might work for you.
WARNING: This also will NOT terminate if your initial response object contains an event with an event title that is not in the eventList array. In that case, the loop will execute indefinitely and will just keep adding "filler" events to the new response array.
let eventList = [ "XM1", "XM2", "XM3" ];
let response = [
{ "id": 1, "eventTitle": "XM1" },
{ "id": 2, "eventTitle": "XM3" },
{ "id": 3, "eventTitle": "XM2" },
{ "id": 4, "eventTitle": "XM2" },
]
let newResponse = [];
let i = 0;
while(true) {
// Get the event title we are looking for. We'll have to manage looping through event titles kind of manually.
let currentEventTitle = eventList[i];
// Grab the index of the NEXT event that matches the current event title.
let nextEventIndex = response.findIndex( event => event.eventTitle === currentEventTitle );
// If our response has an event that matches, return that event from the current response array, otherwise we'll construct a "filler" event
let nextEvent = nextEventIndex !== -1 ? response.splice(nextEventIndex, 1)[0] : { "id": 0, "eventTitle": currentEventTitle };
// Push the found or constructed event to a new array.
newResponse.push(nextEvent);
// Now we'll need to manage our eventlist looping and exit condition.
// First increment our eventList index or start again from 0 if we've reached the end.
i++;
if(i === eventList.length) i = 0;
// Our exit condition is 1) if our starting response array is empty and 2) if we've gotten to the end of our event list
// which at this point means we've looped back around and our index is 0 again.
if(response.length === 0 && i === 0) break;
}
document.getElementById("result").innerText = JSON.stringify(newResponse, null, 4);
<pre id="result"></pre>

Related

Splitting function arguments by a null?

Say my function is called as,
my_function(1, 2, 5, null, 4, null, 1, 3);
and I want to split it into separate segments that last up until a null.
Here's what I have so far:
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
let j = 0;
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray[j].push(theArgs[i]); //breaks here
} else {
_theArgs[j].push(_tempArray); //and here
_tempArray = [];
j++;
}
}
return _theArgs;
}
my_function(1, 2, 5, null, 4, null, 1, 3);
So here I am trying to cycle through each argument passed to the function and split it up into a 2D array. For instance, my_function(1, 2, 5, null, 4, null, 1, 3); would return an array _theArgs where _theArgs[0] = [1, 2, 5], _theArgs[1] = [4] and _theArgs[2] = [1, 3]
I've indicated where my code breaks.. any suggestion to approaching this would be much appreciated
You could search for null and push the parts to the result array.
function fn(...args) {
var result = [],
r = 0,
l;
while ((l = args.indexOf(null, l)) !== -1) {
result.push(args.slice(r, l));
r = l + 1;
l += 2;
}
result.push(args.slice(r));
return result;
}
console.log(fn(1, 2, 5, null, 4, null, 1, 3));
_tempArray[j].push() fails because _tempArray[j] is not an array. _tempArray is initially an empty array, there's nothing in _tempArray[j], so you can't push onto it. I think you just want _tempArray.push(theArgs[i]).
The same with _theArgs[j].
You also need to push onto _theArgs at the end of the function, to get arguments after the last null.
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] !== null) {
_tempArray.push(theArgs[i]);
} else {
_theArgs.push(_tempArray);
_tempArray = [];
}
}
if (_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
This is what you need: (Since you are pushing you don't need the variable j)
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray.push(theArgs[i]); //breaks here
} else {
_theArgs.push(_tempArray); //and here
_tempArray = [];
}
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
It looks like you're trying to call push() on an undefined nested array object (that is being accessed via the j variable).
Consider making the following changes:
const my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
// Push to _tempArray directly. No need to access via "j"
_tempArray.push(theArgs[i]);
} else {
// Push to _theArgs directly. No need to access via "j"
_theArgs.push(_tempArray);
_tempArray = [];
}
}
// Include last args items (if any after final null)
if(_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
const result = my_function(1, 2, 5, null, 4, null, 1, 3);
console.log(result)
The j index is throwing you off; remove it in both cases (it's an undefined offset; push will create the new element automatically).
Also, you'll need to append the final _tempArray before returning the result to ensure the last row is added.
Here's a cleaner version which avoids indexes entirely:
const myFunction = (...args) => {
let row = [];
const result = args.reduce((a, e) => {
if (e === null) {
a.push(row);
row = [];
}
else {
row.push(e);
}
return a;
}, []);
return row.length ? result.concat([row]) : result;
}
console.log(myFunction(1, 2, 5, null, 4, null, 1, 3));
Another Solution:
function my_function(...args) {
var i = 0, temp = [], lastElt = null;
for (j = 0; j < args.length; j++) {
if (args[j] !== null) {
if (temp[i] === undefined) {
temp[i] = [];
}
temp[i].push(args[j]);
} else if (lastElt !== null) {
i++;
}
lastElt = args[j];
}
return temp;
}
var test = my_function(null, 1, 2, 5, null, null, 4, null, null, null, 1, 3);
console.log(test);
As covered in earlier answers the problem is caused by processing a second dimension of _theArgs and _tempArray when only one (or the first) dimension should be used.
Removing two occurences of [j] and the j++; line will fix this but needs patch-up code to include an array of arguments that follow the last null argument.
You could use Array.prototype.reduce to avoid explicit loop code and patchups as may be required:
const args2D = (...argList) =>
argList.reduce(
(result, arg) => {
if (arg === null){
result.push( [] );
}
else {
result[ result.length-1].push( arg);
}
return result;
},
[[]]
);
console.log( args2D(1, 2, 5, null, 4, null, 1, 3));
This approach always produces a two dimensional array, with empty inner array(s) if the argument list is empty, or no non-null values follow a null value in the list.
Short and Sweet - not recommended for server-side scripts because its not as performant as a for loop solution but for the client you may save some bandwidth. Also note that this solution, as presented, only works with numbers, nulls and modern browsers.
my_function = (...theArgs) => {
var _theArgs = theArgs.toString().split(',,');
for(arg in _theArgs) {
_theArgs[arg] = _theArgs[arg].split(',').map(function(v) {return +v })
}
return _theArgs;
};
console.log(my_function(1, 2, 5, null, 4, null, 1, 3) );

How to make string to json with javascript? (like A//a1,A//a2,A//a3//a31 ..)

How to convert a string to JSON with javascript or jQuery? I've been thinking all day, but I do not get a good idea.
This task is to dynamically create the treeview in the client side (ASP.Net). My idea is to convert the string to an object and convert to JSON type. (String -> object -> JSON) I tried, but the day is gone. It is difficult to construct 2 more depth like A->a3->a31.
String is
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
and JSON format is
{
"title": "A",
"key": "1",
"folder": true,
"children": [{
"title": "a1",
"key": "2"
}, {
"title": "a2",
"key": "3"
}, {
"title": "a3",
"key": "4",
"folder": true,
"children": [{
"title": "a31",
"key": "5"
}...
}]
}
(This is fancytreeview plugin)
‘//‘ is depth and ‘,’ is split.
Please help me..
Edit)
I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string.
Please understand that my sentence is strange because my native language is not English.
Edit2)
oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If It’s can, let me know how.
I believe this can be done without recursion:
var string = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
// Take all the roots
var roots = string.split(',');
// We will attach it to every node and keep it incrementing
var key = 1;
// The final result will be in this object
var result = [];
// Loop through to found roots
roots.forEach(function(root) {
// Take all the children
var items = root.split('//');
var parent = result;
// Loop through the available children
items.forEach(function(item, i) {
// Find if the current item exists in the tree
var child = getChild(parent, item);
if (!child) {
child = {
title: item,
key: key++
}
// This will ensure that the current node is a folder only
// if there are more children
if (i < items.length - 1) {
child.folder = true;
child.children = [];
}
// Attach this node to parent
parent.push(child);
}
parent = child.children;
});
});
console.log(result);
// Utility function to find a node in a collection of nodes by title
function getChild(parent, title) {
for (var i = 0; i < parent.length; i++) {
if (parent[i].title === title) {
return parent[i];
}
}
}
This is the draft code which came in my mind at first. I believe it can be improved further in terms of complexity.
var key = 1; // keys start at 1
let addPaths = (root, paths) => {
if (!paths || paths.length == 0)
return;
let path = paths.shift();
//add nodes for the current path
addNodes(root, path.split('//'));
// keep going until all paths have been processed
addPaths(root, paths);
};
let addNodes = (root, nodeList) => {
if (!nodeList || nodeList.length == 0)
return;
let title = nodeList.shift();
// find node under root with matching title
let isRootNode = Array.isArray(root);
node = (isRootNode ? root : root.children || []).find((node) => {
return node.title == title;
});
if (!node){
node = {
title: title,
key: key++
}
// are we at root of object?
if (isRootNode)
root.push(node);
else
{
if (!root.children)
root.children = [];
root.children.push(node);
root.folder = true;
}
}
addNodes(node, nodeList);
};
let parse = (string) => {
let object = [];
let nodes = string.split(',');
addPaths(object, nodes);
return object
};
console.log(JSON.stringify(parse("A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2"), null, 2));
Which results in:
[
{
"title": "A",
"key": 1,
"children": [
{
"title": "a1",
"key": 2
},
{
"title": "a2",
"key": 3
},
{
"title": "a3",
"key": 4,
"children": [
{
"title": "a31",
"key": 5
},
{
"title": "a32",
"key": 6
}
],
"folder": true
}
],
"folder": true
},
{
"title": "B",
"key": 7
},
{
"title": "C",
"key": 8,
"children": [
{
"title": "c1",
"key": 9
},
{
"title": "c2",
"key": 10
}
],
"folder": true
}
]
Try below code. I have used associative array to store already processed folder for faster lookup.
I hope it helps you.
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
var sArr = sString.split(","); // We will split it by comma so that we can iterate through its items.
var output = []; // Final result will be stored here.
var hash = {}; // It used to keep track of itemObjectect's position for faster lookup.
var counter = 1; // Its value will be used to assign to key;
for(var i = 0; i < sArr.length; i++){
var items = sArr[i].split("//");
var itemObject = {}; // Object to store value of each item.
var parentItemObject = {}; // It will refer to current parentObject during iteration.
for(var j = 0; j < items.length; j++){
// Check if item is already processed and stored in hash map.
if(hash.hasOwnProperty(items[j])){
// Check if parent Object value is empty then we will fetch it from hash directly.
if(isEmpty(parentItemObject)){
parentItemObject = output[hash[items[j]]];
}
else{
// It is parent element but is child of another element. Then we will fetch it from it's children array.
if(typeof parentItemObject.children !== "undefined"){
parentItemObject = parentItemObject.children[hash[items[j]]];
}
}
continue;
}
itemObject.title = items[j];
itemObject.key = counter++;
// Check if it is a folder item.
if(j != items.length -1){
itemObject.folder = true;
itemObject.children = [];
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
else{
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
parentItemObject = itemObject;
}
}
else{
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
}
itemObject = {};
}
//console.log(items);
}
function isEmpty(itemObject) {
return Object.keys(itemObject).length === 0;
}
//console.log(hash);
console.log(JSON.stringify(output,null,2));

how to count children in object in javascript?

I have a json array ..I want to refactor the json object ..but I am not able to make as expected output .I want to search all item or object and insert the children of parent ..I try like that
https://jsfiddle.net/nk6v0y36/1/
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
console.log(new_Arry)
Expected output:
[{
"con_id": 11,
"children": [{
"con_to": 12
}]
}, {
"con_id": 12,
"children": [{
"con_to": 13
}, {
"con_to": 14
}]
}, {
"con_id": 13,
"children": []
}, {
"con_id": 14,
"children": [{
"con_to": 15
}]
}, {
"con_id": 15,
"children": [{
"con_to": 16
}]
}, {
"con_id": 16,
"children": []
}]
After answer
https://jsfiddle.net/nk6v0y36/7/
Answer not working..
As thisOneGuy said, you are not checking if a con_id already exists in the new array, so for each element in the old array you are creating a corresponding new one. Your expected output though shows you want to merge these multiple con_id values into a single object.
You are also creating objects with con_to properties in the children arrays even when there is no such property, which again your expected output suggests you do not way.
One way to fix this would be:
var new_Arry = [];
var new_Hash = [];
for (var i = 0; i < arry.length; i++) {
if (new_Hash[arry[i].con_id] === undefined ) {
var obj = {
con_id: arry[i].con_id,
children: []
};
if( arry[i].con_to !== undefined ) {
obj.children.push({ con_to: arry[i].con_to });
}
new_Hash[obj.con_id] = new_Arry.push(obj) - 1;
} else if( arry[i].con_to !== undefined ) {
new_Arry[new_Hash[arry[i].con_id]].children.push({ con_to: arry[i].con_to });
}
}
console.log(new_Arry);
The key here is the new_Hash array, which uses the con_id value as its key, and stores the respective index in the new_Arry as its value.
So for each con_id in arry it first checks the new_Hash to see if there is an entry for it. If not then there is no entry in new_Arry, so it creates a new one.
Firstly the basic object, with con_id and a children array is created. It then checks whether there is a con_to property in arry, and only if there is does it add one as an object to the children array.
This object is then added to new_Arry and new_Hash updated so that an element for con_id references the new new_Arry index. As the .push() method returns extends an array and returns the new array length, the new index will always be this value minus one.
However if an entry for con_id was found in new_Hash then all you need to do it add the con_to value to the existing children array. Whilst presumably if there are multiple con_id entries in arry they must contain a con_to value, it also checks this just to be safe. The index number for the new_Arry as returned by new_Hash[arry[i].cond_id].
Incidentally, the reason for explicitly checking for undefined rather than seeing if value equates to true or false to check for its existence is a con_id or con_to value of 0 would be treated as false and skipped.
Like this -
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
obj.con_id = arry[i].con_id;
var child = [];
var childrenObj = {};
childrenObj.con_to = arry[i].con_to;
child.push(childrenObj);
obj.children = child;
new_Arry.push(obj)
}
https://jsfiddle.net/nk6v0y36/3/
Here is the correct answer with the correct output : https://jsfiddle.net/nk6v0y36/4/
Basically you need to loop through your array to check if the con_id exists in that array, if it does, just add to children, if it doesnt add a new object.
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
if (new_Arry.length > 0) {
console.log('start')
console.log(new_Arry.length);
var thisBool = false;
for (var j = 0; j <= new_Arry.length; j++) {
console.log('inside')
if (new_Arry[j]) {
if (new_Arry[j].con_id === arry[i].con_id) {
console.log('has children')
new_Arry[j].children[new_Arry[j].children.length] = {
con_to: arry[i].con_to
}
} else {
thisBool = true;
}
}
}
if(thisBool){
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
} else {
console.log('no children')
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
}
Check the output in the console log to see it's correct :)

Checking duplicate in an array that contains objects as an array

I want to check if there exists duplicate outputTypeId in the output array object..
Below is the JSON:
$scope.entities= [
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 4
}
]
},
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 7
}
]
},
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 9
}
]
}
]
Below is the code that I tried but its not going inside the condition after execution..
Let outputTypeId be [7] as I'm checking for multiple outputTypeId's,hence an array
$scope.checkForDuplicateOutputs = (outputTypeId) => {
for (var i = 0; i < $scope.entities.length; i++) {
for (var j = i; j < $scope.entities[i].output[j].length; j++) {
if (outputTypeId.contains($scope.entities[i].output[j].outputTypeId)) {
$scope.isDuplicateOutput = true;
break;
} else {
$scope.isDuplicateOutput = false;
}
}
}
}
function checkForDuplicates(outputTypeIds) {
$scope.isDuplicateOutput = $scope.entities.some(function(entity) { // Loop through entities
return entity.output.some(function(entityOutput) { // Look for any output
return outputTypeIds.indexOf(entityOutput.outputTypeId) != -1; // which is duplicated in 'outputTypeIds'
});
});
}
So this solution uses Array.some - It has a few advantages:
Removes the need to manually break your loops
No need to have i and j variables to keep track of loop counters
No need to duplicate $scope.isDuplicateOutput = <boolean>;
Less lines of code :)
You are breaking only the inner loop with that break statement and the problem is even though the duplicate flag does get set to true, it will be reset to false in the next iteration. Basically, in the end you'll get the result of the last iteration only.
The quickest fix is to use a flag to denote whether the external loop needs to be stopped:
$scope.checkForDuplicateOutputs = (outputTypeId) => {
var breakOut = false; // <--- this is new
for (var i = 0; i < $scope.entities.length; i++) {
if (breakOut) break; // <--- this is new
for (var j = i; j < $scope.entities[i].output[j].length; j++)
if (outputTypeId.contains($scope.entities[i].output[j].outputTypeId)) {
$scope.isDuplicateOutput = true;
breakOut = true; // <--- this is new
break;
} else {
$scope.isDuplicateOutput = false;
}
}
}
}
If you still want to iterate all the entities and have a list of all the duplicates, you can make $scope.isDuplicateOutput an array and just push the duplicate ids into it.

javascript associative array access by key value

I have an array like this:
employees = [
{
"id": 1,
"shift_id": 1,
"days": {
"2012-03-01": 1,
"2012-03-02": 1,
"2012-03-03": 1,
"2012-03-04": 0,
"2012-03-05": 0,
"2012-03-06": 0
}},
{
"id": 2,
"shift_id": 1,
"days": {
"2012-03-01": 0,
"2012-03-02": 1,
"2012-03-03": 1,
"2012-03-04": 1,
"2012-03-05": 1,
"2012-03-06": 0
}},
{
"id": 3,
"shift_id": 2,
"days": {
"2012-03-01": 0,
"2012-03-02": 0,
"2012-03-03": 1,
"2012-03-04": 1,
"2012-03-05": 1,
"2012-03-06": 1
}}
];
is there a way to access an element in this array using the id value?
maybe something in jquery?
like $(employees('id = 1');
Just loop through your array and check for the id:
var yourId = 1;
for (var i = 0, len = employees.length; i < len; i++) {
if (employees[i].id == yourId) {
// ...
}
}
You can use a function like this, which filters the array appropriately:
var getEmployee = function (id) {
return employees.filter(function(i) { return i.id == id; });
};
You can use .grep() method documented here:
var employee = $.grep(employees, function(e) { return e.id == 1 })[0];
Well, there's a jQuery way of doing it:
var findElementById = function(elements, id) {
return $.grep(elements, function(e) { return e.id === id; })[0];
}
Still I wonder why don't you just index the source array by id instead.
Maybe you are looking for something like the below:
$.grep(employees, function(n){return n.id==1});
Or this:
$.each(employee, function(){
if(this["id"] == 2){
console.log(this);
}
});
As far as I am aware, in order to achieve that you would have to loop through them
Array.prototype.getObjectById = function(x){
var catcher = false, i = 0;
while(!catcher){
catcher = this[i].id == x ? this[i] : false;
i++;
}
return catcher;
}
This function should help. It will extend the array object so you can use it as myArray.getObjectbyId(id);
By design, this will return the first object that meets the criteria. You could extend it like so:
Array.prototype.getObjectsById = function(x){
var catcher = [], i = 0;
for(var i = 0; i < this.length; i++){
if(this[i].id == value){
catcher.push(this[i]);
}
i++;
}
return catcher.length == 1 ? catcher[0] : catcher;
}
This will return an array of objects if more than one object matches the criteria.
Array.prototype.getObjectsByAttribute = function(x, criteria){
if(!criteria){criteria = 'id';}
var catcher = [], i = 0;
for(var i = 0; i < this.length; i++){
if(this[i].criteria == value){
catcher.push(this[i]);
}
i++;
}
return catcher.length == 1 ? catcher[0] : catcher;
}
This extends it further to look for any criteria.
I know this question is old, but for future reference if anyone else stumbles upon this question ...
Instead of trying to over-engineer a function to parse/examine your JSON, consider changing the structure of your data to suit its purpose.
Consider the example in the question:
data = [ {
"id": 1,
"shift_id": 1,
"days": {
"2012-03-01": 1,
"2012-03-02": 1,
"2012-03-03": 1,
"2012-03-04": 0,
"2012-03-05": 0,
"2012-03-06": 0
}}, { .. }, {...} ]
Structuring the data in this way only gives you sequential access to the objects with no way to lookup an object by a particular index. Array indices are generally meaningless in this context.
data[0] => { id : 1, .. }
data[1] => { id : 2, .. }
What happens if the id is non-sequential or alphanumeric?
Using an array wont help you search any faster, you'll still have to loop...
Instead consider using a hash table/object:
{
'id1' => { id : 1 , data : { ... } },
'id99' => { id : 99, data : { ... } },
'id2' => { id : 2 , data : { ... } },
}
You can use a string value for the key and get direct access to the data by doing something like:
data['id2'] => { id : 2, ... }
Which will give you direct access to the data you want to find (by id). By simply re-organizing the structure of the data we were able to go from a O(n) search to an O(1) search.
Keep in mind that this method may work better for some solutions than others, and there are a number of other considerations to make.
This is just one approach to how you might solve a problem when you want to lookup data by a unique property.
The accepted answer is great - modified a bit for an AngularJS app:
$rootScope.getObjectsByAttribute = function(inarry,infldnm,infldval){
// This will iterate through a fetchAll sql result set and return rows where fldnm==fldval
// If it finds 1 row it returns a single object, if more than that it returns an array of objects
// Usage: result = $rootScope.getObjectsByAttribute(myarray,'myfldnm',myfldval);
if(!infldnm){infldnm = 'id';}
var catcher = [], i = 0;
for(i = 0; i < inarry.length; i++){
if(inarry[i][infldnm] == infldval){
catcher.push(inarry[i]);
}
}
return catcher.length == 1 ? catcher[0] : catcher;
}

Categories