I have a couple of arrays that looks a bit like these:
arr['a'] = 'val1';
arr['b'] = 'val2';
arr['c'] = 'val3';
The index is not an integer, it is a string. I want to remove arr['b'] from the array completely. I have tried:
arr.splice('b', 1);
It does not work, and it might be because the index in not an integer, according to
w3schools this is the problem "index - Required. An integer".
A possible solution could be looping through all arrays and re-creating them with an integer index and then an array holding the custom indexes as values and the equivalent integer index as its index.
This seems like a tad unnecessary and a waste of resources, is there a smarter more effective and simpler solution?
Preferably an arr.splice that will work with a non-integer index.
I have looked through plenty of posts that covers how to remove elements from arrays by index and values, but none covers how to remove elements using a non-integer index.
Example posts that I have found:
0
1
2
Any and all help is greatly appreciated!
//Edit, used following as a solution.
function aObj() {
this.a = "";
this.b = [];
}
var aObjs = [];
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs[0].a = "val1";
aObjs.splice(1, 1);
Looks a bit different than what I used in my first example, but this is more accurate towards how I used it. May not be the best way to do it, but it works.
Don't use array for string indexes, use objects like bellow
var arr = {} //create a object
arr['a'] = 'val1'; //asign values
arr['b'] = 'val2';
arr['c'] = 'val3';
console.log(arr) //prints {a: "val1", b: "val2", c: "val3"}
delete arr['a'] //delete a key
console.log(arr) // prints {b: "val2", c: "val3"}
Well it does not work, because you are using an array as a dictionary, which it's not. First of all use object for that. Second use delete to remove a property:
var dict = { 'a': 'val1', 'b': 'val2', 'c': 'val3' };
delete dict.a;
As said before, this is not an Array. If it should be an array, it looks like this
var arr = ['val1', 'val2', 'val3'];
Now you can use Array.splice to remove value 'val2':
arr.splice(1,1);
// or
arr.splice(arr.indexOf('val2'),1);
// or even
arr = arr.filter(function (v){ return v !== 'val2'});
If it should be an object, its declariation looks like:
var obj = {a: 'val1', b: 'val2', c: 'val3'};
And if you want to delete 'val2' whilst not knowing the key for it you can loop:
for (var key in obj) {
if (obj[key] === 'val2';
delete obj[key];
}
// or (mis)use Object.keys
Object.keys(obj)
.filter(function(v){
return this[v] === 'val2' ? !(delete this[v]) : true;
}, obj);
Knowing this, you can create a helper method for Objects and Arrays:
function removeByValue(objOrArr, value) {
if (objOrArr instanceof Array && objOrArr.length) {
var found = objOrArr.indexOf(value);
if (found) { objOrArr.splice(found,1); }
}
if (objOrArr instanceof Object) {
var keys = Object.keys(objOrArr);
if (keys.length) {
keys.filter(function(v){
return this[v] === value ? !(delete this[v]) : true;
}, objOrArr);
}
}
return objOrArr;
}
// usage (using previous arr/obj)
removeByValue(arr, 'val2'); // arr now ['val1','val3']
removeByValue(obj, 'val2'); // obj now {a:'val1', c: 'val3'}
Example
Related
I am looking for a short and efficient way to filter objects by key, I have this kind of data-structure:
{"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]}
Now I want to filter by keys, for example by "Key1":
{"Key1":[obj1,obj2,obj3]}
var object = {"Key1":[1,2,3], "Key2":[4,5,6]};
var key1 = object["Key1"];
console.log(key1);
you can use the .filter js function for filter values inside an object
var keys = {"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]};
var objectToFind;
var keyToSearch = keys.filter(function(objects) {
return objects === objectToFind
});
The keyToSearch is an array with all the objects filter by the objectToFind variable.
Remember, in the line return objects === objectToFind is where you have to should your statement. I hope it can help you.
You can create a new object based on some custom filter criteria by using a combination of Object.keys and the array .reduce method. Note this only works in es6:
var myObject = {"Key1":["a","b","c"], "Key2":["e","f","g"]}
function filterObjectByKey(obj, filterFunc) {
return Object.keys(obj).reduce((newObj, key) => {
if (filterFunc(key)) {
newObj[key] = obj[key];
}
return newObj;
}, {});
}
const filteredObj = filterObjectByKey(myObject, x => x === "Key1")
console.log(filteredObj)
Not sure what exactly are you trying to achieve, but if you want to have a set of keys that you would like to get the data for, you have quite a few options, one is:
var keys = ['alpha', 'bravo'];
var objectToFilterOn = {
alpha: 'a',
bravo: 'b',
charlie: 'c'
};
keys.forEach(function(key) {
console.log(objectToFilterOn[key]);
});
Given an object of form listed below, what is best way to obtain keys that contain a particular value? For example in following structure, if we want all the objects that possess property1, we should get object1 and object2 as answer.
Or can this be stored in a specific type of data structure for quick retrieval?
Looping over values of all objects in one approach. I am looking for something faster than that. Would appreciate suggestions.
{
object1: [property1, property2, property3],
object2: [property1],
object3: [property2]
}
If you want a faster data structure for that purpose, then you could turn your object into an ES6 map, like this:
// Sample object:
var obj = {
key1: [1, 2, 3],
key2: [1],
key3: [2]
};
// Turn into map keyed by values
var m = Object.keys(obj).reduce( (m, key) =>
obj[key].reduce( (m, v) => m.set(v, (m.get(v) || []).concat(key)), m ),
new Map
);
// Example use of the map:
console.log('1 occurs in: ', m.get(1));
Try something like this
x = {
object1: ['property1', 'property2', 'property3'],
object2: ['property1'],
object3: ['property2']
}
z = Object.keys(x).filter(function(y) {
return x[y].indexOf('property1') !== -1
})
console.log(z)
This just loops, and filters each key in your object and then does an equality check on the array of values. Kinda
var l = [];
var dict = {...}
for (var key in dict) {
var obj = dict[key];
if (obj.indexOf("property1") != -1) {
l.push(obj);
}
}
l // Here is the result
Just use the filter() function over Object.keys(data) and check if each object contains the value. You can also map() the keys to the value itself if that's what you want.
const data = {
a: [1,2,3],
b: [1],
c: [2]
};
const searchValue = 1;
const keysWithValue = Object.keys(data).filter(key => data[key].includes(searchValue));
const objectsWithValue = keysWithValue.map(key => data[key]);
console.log(keysWithValue, objectsWithValue);
Loop through your array and test each time using this:
if(Obj.hasOwnProperty("<property name>")){
array.push(obj);
}
return array;
Check this code, i think, it will help you:
const list = {
object1: ['a', 'b', 'c'],
object2: ['a'],
object3: ['c']
}
function getData(list, value){
return Object.keys(list).filter( item => list[item].indexOf(value) !== -1)
}
getData(list, 'a');
I have been banging my head against this all night.
I have a service that is returning data that looks like this:
You will see there are Objects with a GUID, nested under a parent object. I need to loop through all the "GUID" objects and get the attributes (i.e., author, content, etc.).
The GUIDs are dynamic (I don't know what they are ahead of time). The attributes below it are known.
I am having trouble figuring out how to target it. I can't seem to successfully use a for or forEach loop on it.
I need to use native JavaScript (i.e. no jQuery for this one).
Here's a possible solution:
var keys = Object.keys(data);
var results =
keys.map(
function(key){
var datum = data[key];
// do whatever with the data.
return {
"author" : data["author"]
}
}
)
// now results is an array of {"author"} objects.
var x = {
'a-45-2455': {
'author': 'Some Name'
}
};
var keys = Object.keys(x);
keys.forEach(function(key,value){
var author = x[key]['author'];
console.log(author);
});
You can access the data in this way.
You can also create another array from the values and use that.
In order to loop through an object use for...in
Since you have not posted the code of object , here is a snippet with dummy object
var x = {
'a-45-2455': {
'author': 'Some Name'
}
}
for(var keys in x){
alert(x[keys].author)
}
If you are using angular try angular.forEach loop to iterate over all GUID's, else you can use for each in javascript. see below code snippet.
var user ={
'1': {
"name":'abc',
"age":26
},
'2': {
"name":'def',
"age":28
}
};
for(var key in user) {
console.log(user[key].name);
}
Here is another way to iterate through json Object
var obj = {a: 1, b: 2, c: {a: 1, b: 2}};
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(obj);
var obj = {a: 1, b: 2, c: {a: 1, b: 2}};
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(obj);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I believe that you can iterate through all indexes using the advanced for loop. a.b is the same as a["b"] in javascript.
You can iterate thru the object properties like this:
for(let key in Response){
if(!Response.hasOwnProperty(key))
continue;
//key will be each GUID
let yourObject = Response[key] //Each object in the list of objects
}
You can read about for...in loops here
Hope that helps!
I am using _.isEqual that compares 2 array of objects (ex:10 properties each object), and it is working fine.
Now there are 2 properties (creation and deletion) that i need not to be a part of comparison.
Example:
var obj1 = {name: "James", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
var obj2 = {name: "Maria", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
// lodash method...
_.isEqual(firstArray, secondArray)
You can use omit() to remove specific properties in an object.
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
var obj1 = {
name: "James",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var obj2 = {
name: "Maria",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
#ryeballar's answer is not great for large objects because you are creating a deep copy of each object every time you do the comparison.
It's better to use isEqualWith. For example, to ignore differences in the "creation" and "deletion" properties:
var result = _.isEqualWith(obj1, obj2, (value1, value2, key) => {
return key === "creation" || key === "deletion" ? true : undefined;
});
EDIT (important caveat pointed out in the comments): if objects have different numbers of keys, then isEqualWith considers them to be different, regadless of what your customizer does. Therefore do not use this approach if you want to ignore an optional property. Instead, consider using _.isMatch(), _.isMatchWith(), or #ryeballar's _.omit() approach.
Note that if you're writing for ES5 and earlier, you'll have to replace the arrow syntax (() => {) with function syntax (function() {)
_.omit creates deep copy of the object. If you need to exclude only root props it is better to create shallow copy using, for example, destructuring assignment:
const x = { a: 4, b: [1, 2], c: 'foo' }
const y = { a: 4, b: [1, 2], c: 'bar' }
const { c: xC, ...xWithoutC } = x
const { c: yC, ...yWithoutC } = y
_.isEqual(xWithoutC, yWithoutC) // true
xWithoutC.b === x.b // true, would be false if you use _.omit
Best way is not to create copies at all (TypeScript):
function deepEqual(
x?: object | null,
y?: object | null,
ignoreRootProps?: Set<string>
) {
if (x == null || y == null) return x === y
const keys = Object.keys(x)
if (!_.isEqual(keys, Object.keys(y)) return false
for (let key of keys) {
if (ignoreRootProps && ignoreRootProps.has(key)) continue
if (!_.isEqual(x[key], y[key])) return false
}
return true
}
You could map your array into a "cleaned" array, then compare those.
// Create a function, to do some cleaning of the objects.
var clean = function(obj) {
return {name: obj.name, age: obj.age};
};
// Create two new arrays, which are mapped, 'cleaned' copies of the original arrays.
var array1 = firstArray.map(clean);
var array2 = secondArray.map(clean);
// Compare the new arrays.
_.isEqual(array1, array2);
This has the downside that the clean function will need to be updated if the objects are expecting any new properties. It is possible to edit it so that it removes the two unwanted properties instead.
I see two options.
1) Make a second copy of each object that doesn't contain the creation or date.
2) Loop through all the properties and, assuming you know for certain that they both have the same properties, try something like this.
var x ={}
var y ={}
for (var property in x) {
if(property!="creation" || property!="deletion"){
if (x.hasOwnProperty(property)) {
compare(x[property], y[property])
}
}
}
Where compare() is some simple string or object comparison. If you are certain of the properties on one or both the objects, you can simplify this code a bit further, but this should work in most cases.
My final solution required a full comparison ignoring an optional property so the above solutions did not work.
I used a shallow clone to remove the keys I wanted to ignore from each object before comparing with isEqual:
const equalIgnoring = (newItems, originalItems) => newItems.length === originalItems.length
&& newItems.every((newItem, index) => {
const rest1 = { ...newItem };
delete rest1.creation;
delete rest1.deletion;
const rest2 = { ...originalItems[index] };
delete rest2.creation;
delete rest2.deletion;
return isEqual(rest1, rest2);
});
If you want to check a subset for each item in the array this works:
const equalIgnoringExtraKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatch(fullObj, partialObjs[index]));
If you also want to ignore a specific property and check subset:
const subsetIgnoringKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatchWith(
fullObj,
partialObjs[index],
(objValue, srcValue, key, object, source) => {
if (["creation", "deletion"].includes(key)) {
return true;
}
return undefined;
}
));
Consider:
var object = {
foo: {},
bar: {},
baz: {}
}
How would I do this:
var first = object[0];
console.log(first);
Obviously, that doesn’t work because the first index is named foo,
not 0.
console.log(object['foo']);
works, but I don’t know it’s named foo. It could be named anything. I just want the first.
Just for fun this works in JS 1.8.5
var obj = {a: 1, b: 2, c: 3};
Object.keys(obj)[0]; // "a"
This matches the same order that you would see doing
for (o in obj) { ... }
If you want something concise try:
for (first in obj) break;
alert(first);
wrapped as a function:
function first(obj) {
for (var a in obj) return a;
}
they're not really ordered, but you can do:
var first;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof(i) !== 'function') {
first = obj[i];
break;
}
}
the .hasOwnProperty() is important to ignore prototyped objects.
This will not give you the first one as javascript objects are unordered, however this is fine in some cases.
myObject[Object.keys(myObject)[0]]
If the order of the objects is significant, you should revise your JSON schema to store the objects in an array:
[
{"name":"foo", ...},
{"name":"bar", ...},
{"name":"baz", ...}
]
or maybe:
[
["foo", {}],
["bar", {}],
["baz", {}]
]
As Ben Alpert points out, properties of Javascript objects are unordered, and your code is broken if you expect them to enumerate in the same order that they are specified in the object literal—there is no "first" property.
for first key of object you can use
console.log(Object.keys(object)[0]);//print key's name
for value
console.log(object[Object.keys(object)[0]]);//print key's value
There is no way to get the first element, seeing as "hashes" (objects) in JavaScript have unordered properties. Your best bet is to store the keys in an array:
var keys = ["foo", "bar", "baz"];
Then use that to get the proper value:
object[keys[0]]
ES6
const [first] = Object.keys(obj)
Using underscore you can use _.pairs to get the first object entry as a key value pair as follows:
_.pairs(obj)[0]
Then the key would be available with a further [0] subscript, the value with [1]
I had the same problem yesterday. I solved it like this:
var obj = {
foo:{},
bar:{},
baz:{}
},
first = null,
key = null;
for (var key in obj) {
first = obj[key];
if(typeof(first) !== 'function') {
break;
}
}
// first is the first enumerated property, and key it's corresponding key.
Not the most elegant solution, and I am pretty sure that it may yield different results in different browsers (i.e. the specs says that enumeration is not required to enumerate the properties in the same order as they were defined). However, I only had a single property in my object so that was a non-issue. I just needed the first key.
You could do something like this:
var object = {
foo:{a:'first'},
bar:{},
baz:{}
}
function getAttributeByIndex(obj, index){
var i = 0;
for (var attr in obj){
if (index === i){
return obj[attr];
}
i++;
}
return null;
}
var first = getAttributeByIndex(object, 0); // returns the value of the
// first (0 index) attribute
// of the object ( {a:'first'} )
To get the first key of your object
const myObject = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
}
const result = Object.keys(myObject)[0];
// result will return 'foo1'
Based on CMS answer. I don't get the value directly, instead I take the key at its index and use this to get the value:
Object.keyAt = function(obj, index) {
var i = 0;
for (var key in obj) {
if ((index || 0) === i++) return key;
}
};
var obj = {
foo: '1st',
bar: '2nd',
baz: '3rd'
};
var key = Object.keyAt(obj, 1);
var val = obj[key];
console.log(key); // => 'bar'
console.log(val); // => '2nd'
My solution:
Object.prototype.__index = function(index)
{
var i = -1;
for (var key in this)
{
if (this.hasOwnProperty(key) && typeof(this[key])!=='function')
++i;
if (i >= index)
return this[key];
}
return null;
}
aObj = {'jack':3, 'peter':4, '5':'col', 'kk':function(){alert('hell');}, 'till':'ding'};
alert(aObj.__index(4));