(js) how to simultaneously iterate through an object while iterating through values - javascript

I am basically trying to get this problem to work and have isolated the issue to line 21. I think the way I'm trying to access the object key is wrong. I am simply trying to say: if the object key in the new object exists in the original array, push the new value from the new object into the new array.
Edit to add code block
function valueReplace(array, obj) {
var replaced = [];
for (var i = 0; i < array.length; i += 1) {
var value = obj[array[i]];
if (array.indexOf(obj.i) !== -1) {
replaced.push(value);
} else {
replaced.push(array[i]);
}
}
return replaced;
}

You have a mixed up error report, but at the actual code, you try to access the object with the property i, obj.i, which not exists. Read more about property accessor.
For getting the wanted result, you might use the in operator for checking if a property in an object exists.
if (array[i] in obj) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}

It looks like one of the issues you are having is trying to access a dynamic property with dot notation, which JS generally doesn't like. I reversed your logical if because IMO it makes more sense to see if the object has a property than get a property and then get the index of the array, but you could reverse it back to using index of by array.indexOf(obj[i]) !== -1
function valueReplace(array, obj) {
let replaced = [];
for (let i = 0, len = array.length; i < len; i++) {
if (obj.hasOwnProperty(array[i])) {
replaced.push(obj[array[i]]);
} else {
replaced.push(array[i]);
}
}
return replaced;
}
Because I generally like simplifying things here is this functionality rewritten in ES6 compatible code, using array.prototype.map. Don't use it for your homework, but if you want you can work it backwards into a standard function ;).
const valueReplace = (array, obj) => array.map(val => (obj.hasOwnProperty(val)) ? obj[val] : val);

Related

Check if object is in AngularJS array that is in localStorage

I have this angularjs array: $scope.fav = [] where items (objects) get added with to it on a function call. an example of these objects are {quote: "Some text", controller: some_controller} When I add a new object to the array, the array is saved in localstorage. The array works exactly how I want it to except when I try to print it in console.log() i get many [object, Object], which I just assumed is the way it is printed. This isn't the main issue though because the array works how it needs to.
The issue I am having is with trying to find if an object is already in an array. I have tried
if ($scope.fav.indexOf({quote: q, controller: c}) == -1)
and this did not seem to work because every object going in was index -1 even if it was already in the array. I presume this is because it is not reading the object correctly.
Lastly I have resorted to this function:
$scope.containsObject = function(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (list[i] === obj) {
return true;
}
}
return false;
}
which checks if the object is in the array. Here is how I call it:
$scope.addToFav = function(q, c) {
$scope.value = $scope.containsObject({quote: q, controller: c}, $scope.fav)
console.log($scope.value);
}
I keep getting a negative value for $scope.value even if the object is in the array.
Sorry for the long complicating explanation.
Thank you for your insight,
Ben
Array.indexOf() and the === operator compare object references and are only true when comparing references to the same instance of an object. {quote: q, controller: c} is an entirely different object instance than those in the array, even if its properties match an object in the array exactly.
Angular has a helper function called angular.equals() that checks of 2 objects are equivalent. You can use it in place of the ===...
$scope.containsObject = function(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (angular.equals(list[i], obj)) {
return true;
}
}
return false;
};
Each time you're retrieving an object from local storage, you're actually creating a new object, probably using JSON.decode, to convert the string data stored in local storage, into an object.
Even if the new object contains the same data, it will fail the strict test === (and indeed, the loose ==) when compared to the existing (albeit apparently identical) object. indexOf uses the strict equality operator === so this would behave the same way.
So you need some code to test if an object is equal to another, and then apply it to a list. One way is to use a combination of angular.equals, which performs a deep comparison of objects, and filter:
$scope.containsObject = function(obj, list) {
return list.filter(function(listItem) {
return angular.equals(listItem, obj)
}).length > 0;
}
The filter function I don't believe is part of IE8. If you need IE8 supported, you can use the polyfill, or use another library, like lo-dash
$scope.containsObject = function(obj, list) {
return _.filter(list, function(listItem) {
return angular.equals(listItem, obj)
}).length > 0;
}

write a function that gets passed an object and returns an array of the object's properties

My homework is like:
Write a "keys" function that gets passed an object and returns an array of the object's properties. Be sure to screen out the object's methods. The keys array only has names of the object's name/value pairs. Because of cross browser issues(non-support in older browsers), the Objectkeys method cannot be used. Your function should provide the same service for all the browsers.
My initial code is as below:
function keys(obj){
var key="";
var i = 0;
var array = [];
for(i = 1; i<arguments.length; i++){
for(key in arguments[i]){
if(obj.hasOwnProperty&&(!_.isArray(obj))){
obj[key]=arguments[i][key];
}
}
}
for(var j = 0; j < obj.length; j++){
for(key in obj[j]){
array[j] = obj[j];
}
}
return array;
}
I'm pretty sure that my function has a lot of problems. Could you please help me with it? Thank you!
This is the solution:
function keys(obj) {
var hasOwnProperty = Object.prototype.hasOwnProperty;
var properties = [];
for (var property in obj)
if (hasOwnProperty.call(obj, property)
&& typeof obj[property] !== "function")
properties.push(property);
return properties;
}
Line by line the above code does the following:
Create an empty array properties to hold the names of all the properties of obj.
For each property property of obj do:
If the property property belongs to obj and
If obj[property] is not a function then:
Add the property property to the properties array.
Return the properties array.
See the demo: http://jsfiddle.net/qVgVn/
There are a lot of problems with your code. The answer you need is here in the MDN though: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
That function does EXACTLY what your professor asked, and it does it cross-browser. There is what is called a poly-fill, or cross-browser implementation of object.keys listed under "Compatability". Try to sort out that code to figure out what it's doing :)
Here are some problems with your own code that I see right off the bat - it's probably not working code, I just wanted to give you some guidance of things you did incorrectly:
// Name your function something useful and descriptive.
function getKeysAsArray(obj){
// For starters, dont name a variable "array" - bad practice.
var key="",
i = 0,
results = [];
// First foor loop unnecessary, do not use arguments here
// because you already know the name of your argument.
for(key in obj){
// See if this browser supports has OwnProperty by using typeof
// which will fail gracefully, vs what u did which will stop the
// script from running
if(typeof Object.hasOwnProperty === 'function'){
// You probably shouldn't be using underscore _
if(obj.hasOwnProperty && !(obj instanceof Array)){
results.push(obj[key]);
}
}
}
return results;
}
ok here i go...
function objProps(x){
var arr=[];
for (var k in x) if(typeof x[k] !='function' && x.hasOwnProperty(k)) {arr.push(k);}
return arr;
}
this code works as expected. call it with object...
get its only keys out that are NOT Functions.

jQuery: Index of element in array where predicate

I have an array of objects. Each object has, among others, an ID attribute. I want to find the index in the array of the object with a specific ID. Is there any elegant and simple way to do this in jQuery?
See [`Array.filter`][1] to filter an array with a callback function. Each object in the array will be passed to the callback function one by one. The callback function must return `true` if the value is to be included, or false if not.
var matchingIDs = objects.filter(function(o) {
return o.ID == searchTerm;
});
All objects having the ID as searchTerm will be returned as an array to matchingIDs. Get the matching element from the first index (assuming ID is unique and there's only gonna be one)
matchingIDs[0];
[1]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
Update:
Checkout findIndex from ECMAScript 6.
items.findIndex(function(item) { item.property == valueToSearch; });
Since findIndex isn't available on most browsers yet, you could backfill it using this implementation:
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
In the case you should use for loop in javascript instead of using jQuery. See way 3 in http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/
UPDATED: jQuery is written in javascript and it can not be faster than another code written also in javascript. jQuery is very good if you work with the DOM, but doesn't really help if you're working with simple javascript arrays or objects.
The code you're looking for can be something like this:
for (var i=0, l = ar.length; i<l; i++) {
if (ar[i].ID === specificID) {
// i is the index. You can use it here directly or make a break
// and use i after the loop (variables in javascript declared
// in a block can be used anywhere in the same function)
break;
}
}
if (i<l) {
// i is the index
}
Important that you should hold some simple javascript rules: Always declare local variables (don't forget var before variable declaration) and cache any properties or indexes that you use more than one time in a local variable (like ar.length above). (See for example http://wiki.forum.nokia.com/index.php/JavaScript_Performance_Best_Practices)
Not really elegant, but a cute trick:
var index = parseInt(
$.map(array, function(i, o) { return o.id === target ? i : ''; }).join('')
);
jQuery doesn't have a lot of functional constructs like that; the philosophy of the library is really focused on the job of DOM wrangling. They won't even add a .reduce() function because nobody can think of a reason it'd be useful to the core functionality.
The Underscore.js library has a lot of such facilities, and it "plays nice" with jQuery.
There are no built-in methods for this; the [].indexOf() method doesn't take a predicate, so you need something custom:
function indexOf(array, predicate)
{
for (var i = 0, n = array.length; i != n; ++i) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
var index = indexOf(arr, function(item) {
return item.ID == 'foo';
});
The function returns -1 if the predicate never yields a truthy value.
Update
There's Array.findIndex() that you could use now:
const arr = [{ID: 'bar'}, {ID: 'baz'}, {ID: 'foo'}];
const index = arr.findIndex(item => item.ID === 'foo');
console.log(index); // 2
Use jOrder. http://github.com/danstocker/jorder
Feed your array into a jOrder table, and add an index on the 'ID' field.
var table = jOrder(data)
.index('id', ['ID']);
Then, get the array index of an element by:
var arrayidx = table.index('id').lookup([{ ID: MyID }]);
If you want the entire row, then:
var filtered = table.where([{ ID: MyID }]);
Voila.

Remove element from Javascript associative array using array value

I am trying to remove an element from a Javascript associtive array using the value to find it, but I am having trouble. I have tried splice and JQuery's grep method and neither have worked for me. This is what I currently have.
var array_path = new Array();
function bulk_upload(){
var temp_array = new Object();
for (var i = 1; i<8; i++){
temp_array[i] = $('#path' + i).val();
if(temp_array[i]!='' && temp_array[i]!=null){
array_path['path' + i] = $('#path' + i).val();
}
}
process_txt();
}
function process_txt(){
//alert(array_path.indexOf(full_path)); //returns nothing
var removed_element = array_path.splice(getKey(array_path), 1);
//array_path = $.grep(array_path, function(val) { return val != full_path; });
alert(removed_element);//return nothing, just blank alert box
}
function getKey(data) {
for (var prop in data)
return prop;
}
The way to do this is to use the delete operator.
delete array_path[getKey(array_path)]
Some Background Information
In JavaScript, almost everything descends from Object.prototype. JavaScript, being an open and dynamic language allows you to create/modify properties of objects by simple assignment. This is very similar to what an associative array -- a structure that contains keyed values.
Under the hood an array is just an object that descends from Array.prototype with numeric keys and a special property called length. The length property just returns one greater than the highest numeric property. In essence, an Array is an object with different semantics.
If you're wanting an associative array then Array is not the object you want to descend from. You would want to descend directly from Object. There are two ways to do that, you could either use the new operator or an empty object literal. The syntax for both is below:
var o = new Object();
var o = {};
The second is preferred since it's a little bit more concise.
I wrote a blog post about this a while back, have a look if you want a little bit more info.
There is no such thing in JavaScript as an "associative array" per se. The data structure which corresponds to this concept is simply a JavaScript Object.
Of course, a JavaScript Array (like essentially everything in JavaScript) is an Object, but one with additional capabilities. So you can use an Array as a key-value map, but it's really not the correct structure for that.
To remove a key from an Object, you just do something like this:
var myObj = {};
var myKey = "blah";
myObj[myKey] = 1234; // Adds or updates value for "blah" to 1234.
delete myObj[myKey]; // Removes key-value pair for "blah".
Have you tried delete hash.someKey; ?
You can give your object a remove method, or use apply or call to use another object's remove method, if defined.
function myObj(members){
for(var p in members) this[p]= members[p];
}
myObj.prototype.remove= function(val){
for(var p in this){
if(this[p]=== val) delete this[p];
}
return this;
}
myObj.prototype.toString= function(){
var A= [];;
for(var p in this){
if(this.hasOwnProperty(p)){
A.push(p+':'+this[p])
}
}
return '{'+A.join(', ')+'}';
}
var O= new myObj({a: 1, b: 10, c: 100});
alert(O)
O.remove(10);
alert(O)
I'm not psychic, so I can only guess that you wanted to accomplish something like this:
var paths = [];
function getPaths() {
for(var i = 1; i < 8; ++i) {
var value = $('#path' + i).val();
if(value) paths.push(value);
}
}
function process() {
var firstPath = paths.shift();
// do stuff
}
getPaths();
if(paths.length) process();

JavaScript Array Iteration returning more than values

This is so simple I am baffled. I have the following:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
for (t in stypes) {
if (stypes[t] != x) {
alert(stypes[t]);
}
}
Once the values have iterated it starts returning a dozen functions like
function (iterator, context) {
var index = 0;
iterator = iterator.bind(context);
try {
this._each(function (value) {iterator(value, index++);});
} catch (e) {
if (e != $break) {
throw e;
}
}
return this;
}
What the heck is going on?
Edit: In these scripts I am using http://script.aculo.us/prototype.js and http://script.aculo.us/scriptaculous.js I remember now reading about the way prototype extends arrays and I am betting this is part of it. How do I deal with it?
The for enumeration is going to go over every member of the object you passed it. In this case an array, which happens to have functions as members as well as the elements passed.
You could re-write your for loop to check if typeof stypes[t] == "function" or yada yada. But IMO you are better off just modifying your looping to only elements..
for(var i = 0, t; t = stypes[i]; ++i){
if (t != x) {
alert(t);
}
}
Or
for(var i = 0; i < stypes.length; ++i){
if (stypes[i] != x) {
alert(stypes[i]);
}
}
I wanted to migrate my last comment up to the answer to add the notice of the a caveat for the first type of loop.
from Simon Willison's "A re-introduction to JavaScript"..
for (var i = 0, item; item = a[i]; i++) {
// Do something with item
}
Here we are setting up two variables.
The assignment in the middle part of
the for loop is also tested for
truthfulness - if it succeeds, the
loop continues. Since i is incremented
each time, items from the array will
be assigned to item in sequential
order. The loop stops when a "falsy"
item is found (such as undefined).
Note that this trick should only be
used for arrays which you know do not
contain "falsy" values (arrays of
objects or DOM nodes for example). If
you are iterating over numeric data
that might include a 0 or string data
that might include the empty string
you should use the i, j idiom instead.
you want to do:
for (var i in object) {
if (!object.hasOwnProperty(i))
continue;
... do stuff ...
}
As for..in enumeration iterates over all properties (enumerable or otherwise) that exist on both the object and its prototype chain. The hasOwnProperty check restricts iteration to just those properties on the actual object you want to enumerate.
ES5 makes things a little better for library developers (and help avoid this stuff) but we won't see that ina shipping browser for quite a while :-(
[edit: replacing return with continue. lalalalala ;) ]
Since prototype has extended the array for your convenience you should take advantage of it. Your example could be rewritten as:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
stypes.without(x).each(alert);
It should be
for (t in stypes) {
if (t != x) {
alert(t);
}
}

Categories