$.each(constructions, function(i,v) {
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};
Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.
The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<
Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.
Instead of checking for -1, you check whether the array that $.grep() returns has length == 0
Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)
var constructions = [{
Name: "Mess hall",
SqFt: 5000
}, {
Name: "Infirmary",
SqFt: 2000
}, {
Name: "Bungalow",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
var buildings = [{
Name: "Infirmary",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
// found buildings will be list of items in "constructions" that is not in "buildings"
var foundBuildings = $.grep(constructions, function (constructionsItem) {
return $.grep(buildings, function (buildingsItem) {
return buildingsItem.Name === constructionsItem.Name
}).length == 0; // == 0 means "not in", and > 0 means "in"
});
// this just renders the results all pretty for ya
$.each(foundBuildings, function (idx, item) {
$("#output").append("<div>" + item.Name + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>
Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/
The non-jQuery way of doing this would be to use filter. Something like this:
// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
return arr.map(function (el) { return el[key]; });
}
function notFoundIn(arr, arr2) {
// grab the names of the buildings
var buildings = getValues(arr2, 'name');
// grab the names from the construction objects and filter
// those that are not in the building array
return getValues(arr, 'name').filter(function (el) {
return buildings.indexOf(el) === -1;
});
}
notFoundIn(constructions, buildings); // eg [ "one", "three" ]
DEMO
You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):
function getValues(arr, key) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
out.push(arr[i][key]);
}
return out;
}
if (!Array.prototype.notFoundIn) {
Array.prototype.notFoundIn = function (inThisArray, key) {
var thisArr = key ? getValues(this, key) : this;
var arrIn = key ? getValues(inThisArray, key) : inThisArray;
var out = [];
for (var i = 0, l = thisArr.length; i < l; i++) {
if (arrIn.indexOf(thisArr[i]) === -1) {
out.push(thisArr[i]);
}
}
return out;
}
}
constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]
DEMO
Related
I know we can match array values with indexOf in JavaScript. If it matches it wont return -1.
var test = [
1, 2, 3
]
// Returns 2
test.indexOf(3);
Is there a way to match objects? For example?
var test = [
{
name: 'Josh'
}
]
// Would ideally return 0, but of course it's -1.
test.indexOf({ name: 'Josh' });
Since the two objects are distinct (though perhaps equivalent), you can't use indexOf.
You can use findIndex with a callback, and handle the matching based on the properties you want. For instance, to match on all enumerable props:
var target = {name: 'Josh'};
var targetKeys = Object.keys(target);
var index = test.findIndex(function(entry) {
var keys = Object.keys(entry);
return keys.length == targetKeys.length && keys.every(function(key) {
return target.hasOwnProperty(key) && entry[key] === target[key];
});
});
Example:
var test = [
{
name: 'Josh'
}
];
var target = {name: 'Josh'};
var targetKeys = Object.keys(target);
var index = test.findIndex(function(entry) {
var keys = Object.keys(entry);
return keys.length == targetKeys.length && keys.every(function(key) {
return target.hasOwnProperty(key) && entry[key] === target[key];
});
});
console.log(index);
Note that findIndex was added in ES2015, but is fully polyfillable.
Nope, you can't and the explanation is simple. Despite you use the same object literal, two different objects are created. So test would have another reference for the mentioned object if you compare it with the reference you are looking for in indexOf.
This is kind of custom indexOf function. The code just iterates through the items in the object's array and finds the name property of each and then tests for the name you're looking for. Testing for 'Josh' returns 0 and testing for 'Kate' returns 1. Testing for 'Jim' returns -1.
var test = [
{
name: 'Josh'
},
{
name: 'Kate'
}
]
myIndexOf('Kate')
function myIndexOf(name) {
testName = name;
for (var i = 0; i < test.length; i++) {
if(test[i].hasOwnProperty('name')) {
if(test[i].name === testName) {
console.log('name: ' + test[i].name + ' index: ' + i);
return i;
}
}
}
return -1;
}
You can loop on array and then look for what you want
var test = [{ name: 'Josh' }]
const Myname = test.map((item) => { return item.name; }).indexOf("Josh")
I'm tryign to write code that will loop through an array "productsArray" and match it against my productPropertyArray to pull matching information.
however productsArray is an array in an array that contains an object with the data. My Question is how can I loop through both arrays and then return the matching data.
Current function:
var pList = productsArray
if (productPropertyArray.length === 0 || productsArray.length === 0) return [];
for (var i = 0; i < pList.length; i++) {
for (var j = 0; j < pList[i].length; j++) {
if (pList[i][j] === productPropertyArray) {
return productPropertyArray;
} else {
continue;
}
}
}
return [];
};
example of pList:
productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": 1}],
[{"sellAlone": false,"test": 1}]
];
example of productPropertyArray: (its an argument thats replaced by the following)
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
productPropertyArray is just an argument in the function which is replaced by productSKUArray The setup goes like this: function(productProperty, productPropertyArray, productsArray) {
productProperty is just a string that contains sku or productID
any ideas are appreciated. thanks.
Check this out:
http://jsfiddle.net/v9d7bjms/2/
function find() {
var productsArray = [
[{"sku" : "131674"},
{"sku" : "84172"}],
[{"productID" : "1234"}
,{"productID" : "12345"}],
[{"test": 1},{"test": 1}],
[{"test": 1},{"sellAlone": false,"test": 1}],
[{"test": "00544MF24F575"}],
[{"sellAlone": false,"test": 1}]
],
pList = productsArray,
productSKUArray = [
"00544MF24F575",
"131674",
"84172"
];
// All arrays matching your productsSKUArray
var findings = productsArray.filter(function (productProperty) {
// .some returns true after finding first matching element (and breaks the loop)
return productProperty.some(function (obj) {
var keys = Object.keys(obj);
// We need to get all the "values" from object so we interate over
// the keys and check if any value matches something from productSKUArray
return keys.some(function (key) {
// Check if value exists in productsSKUArray
return productSKUArray.indexOf(obj[key]) > -1;
});
});
});
return findings;
}
console.log(find());
.filter will return all arrays containing objects with values from productSKUArray.
See Array.prototype.filter, Array.prototype.some and Array.prototype.indexOf for method reference.
The inner if needs to refer to pList[i][j].
This will output [{sku: "131674"}, {sku: "84172"}].
var matchingData = [];
for(var productProperties in productsArray){
var pp = productsArray[productProperties];
for(var property in pp) {
var p = pp[property];
for(var propertyName in p){
var propertyValue = p[propertyName];
for(var i in productSKUArray){
if(propertyValue == productSKUArray[i]){
matchingData.push(p);
break;
}
}
}
}
}
but this is just the brute force solution.
Given data such as :
var people = [
{ 'myKey': 'John Kenedy', 'status': 1 },
{ 'myKey': 'Steeven Red', 'status': 0 },
{ 'myKey': 'Mary_Kenedy', 'status': 3 },
{ 'myKey': 'Carl Orange', 'status': 0 },
{ 'myKey': 'Lady Purple', 'status': 0 },
... // thousands more
];
How to efficiently get the list of all objects which contains in myKey the string Kenedy ?
http://jsfiddle.net/yb3rdhm8/
Note: I currently use str.search() :
The search("str") returns the position of the match. Returns -1 if no match is found.
to do as follow :
var map_partial_matches = function(object, str){
var list_of_people_with_kenedy = [] ;
for (var j in object) {
if (object[j]["myKey"].search(str) != -1) {
object[j].presidentName = "yes"; // do something to object[j]
list_of_people_with_kenedy.push({ "people": object[j]["myKey"] }); // add object key to new list
}
} return list_of_people_with_kenedy;
}
map_partial_matches(people, "Kenedy");
I could do the same using str.match() :
str.match() returns the matches, as an Array object. Returns null if no match is found.
It works anyway, but I have no idea if it's efficient or completely dump.
You can use filter():
var filtered = people.filter(function (item) {
if (item.myKey.indexOf("Kenedy") != -1)
return item;
});
You can also checkout Sugar.js
In order to search your unsorted object you need to get through all of it's properties - So I'd say a simple loop with an indexOf will be pretty much the best you can go:
var foundItems = [];
for(var i = 0; i < people.length ;i++)
{
if(people[i].myKey.indexOf('Kenedy') > -1)
foundItems.push(people[i]]);
}
Maybe you can tweak it up a little, but it's pretty much the best you can get.
You can write a basic function that uses filter to return an array of matches based on a key and value:
function find(arr, key, val) {
return arr.filter(function (el) {
return el[key].indexOf(val) > -1;
});
}
var result = find(people, 'myKey', 'Kenedy');
Alternatively use a normal for...loop:
function find(arr, key, val) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
if (arr[i][key].indexOf(val) > -1) {
out.push(arr[i]);
}
}
return out;
}
DEMO
Does the Object Contain a Given Key?
function hKey(obj, key) {
arr = [];
// newarr =[];
for(el in obj){
arr.push(el)
} //return arr;
for(i=0; i<arr.length; i++){
name = arr[i]
} if(name == key) {
return true;
}else {
return false;
}
}
console.log(hKey({ a: 44, b: 45, c: 46 }, "c"))
I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}
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;
}