Related
My requirement is to get the json objects from different sources and merge them into one single json object using javascript
example:
Below there are 2 json objects with different values and I want to make it as one final Json object and append new value to the final result
json1= {
"b":"test1",
"a":"test3",
"type":"sample1"
}
json2= {
"endDate":"ddd1",
"startDate":"dd01"
}
Expected results should be like below
result = {
"b":"test1",
"a":"test3",
"type":"sample1",
"endDate":"ddd1",
"startDate":"dd01"
}
Can anyone suggest me the best way to achieve this in javascript please ?
You can use the rest operator to remove type property and then assign values from 1st object to new property of the merged object
let mergeObjects = (a, b) => {
let {type, ...rest} = b; //remove type property
for(let prop in rest){
if(a[prop]){
rest[prop].xyz = a[prop];
}
}
return rest;
}
let Object3= mergeObjects(Object1, Object2);
console.log(Object3);
Can be as simple as this:
let Object1 ={"b":"test1","a":"test3","type":"sample1"};let Object2 ={"b":{"endDate":"ddd1","startDate":"dd01"},"a":{"endDate":"31","startDate":"01"},"type":"sample2"}
let Object3 = Object2;
for (let i in Object3){
Object3[i]["XYZ"] = Object1[i]
}
Object3["type"] = "12345"
console.log(Object3)
I want to make an array based on two arrays - "ideaList" and "endorsements" declared globally. As ideaList and endorsements are used in other parts of the program I need them to be immutable, and I thought that .map and .filter would keep this immutability.
function prepareIdeaArray(){
var preFilteredIdeas=ideaList
.filter(hasIdeaPassedControl)
.map(obj => {obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
.reduce((sum, x)=>sum+x.count,0);
obj.like = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber && x.who===activeUser)
.reduce((sum, x)=>sum+x.count,0)===0?false:true
obj.position = generatePosition(obj.status)
obj.description = obj.description.replace(/\n/g, '<br>')
return obj;});
preFilteredIdeas.sort(compareOn.bind(null,'count',false)).sort(compareOn.bind(null,'position',true))
return preFilteredIdeas;
}
However, when I console.log ideaList after this function has been executed, I remark that objects of the array all have the "count", "like", "position" properties with values, which proves that the array has been mutated.
I tried by using .map only, but same result.
Would you know how I could prevent ideaList to get mutated? Also I would like to avoid to use const, as I declare ideaList globally first, and then assign to it some data in another function.
You're not mutating the array itself but rather the objects that the array contains references to. .map() creates a copy of the array but the references contained in it points to the exact same objects as the original, which you've mutated by adding properties directly to them.
You need to make copies of these objects too and add the properties to these copies. A neat way to do this is to use object spread in .map() callback:
.map(({ ...obj }) => {
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
If your environment doesn't support object spread syntax, clone the object with Object.assign():
.map(originalObj => {
const obj = Object.assign({}, originalObj);
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
In JS, the objects are referenced. When created, in other words, you get the object variable to point to a memory location which intends to be holding a meaningful value.
var o = {foo: 'bar'}
The variable o is now point to a memory which has {foo: bar}.
var p = o;
Now the variable p too is pointing to the same memory location. So, if you change o, it will change p too.
This is what happens inside your function. Even though you use Array methods which wouldn't mutate it's values, the array elements themselves are objects which are being modified inside the functions. It creates a new array - but the elements are pointing to the same old memory locations of the objects.
var a = [{foo: 1}]; //Let's create an array
//Now create another array out of it
var b = a.map(o => {
o.foo = 2;
return o;
})
console.log(a); //{foo: 2}
One way out is to create a new object for your new array during the operation. This can be done with Object.assign or latest spread operator.
a = [{foo: 1}];
b = a.map(o => {
var p = {...o}; //Create a new object
p.foo = 2;
return p;
})
console.log(a); // {foo:1}
To help having immutability in mind you could think of your values as primitives.
1 === 2 // false
'hello' === 'world' // false
you could extend this way of thinking to non-primitives as well
[1, 2, 3] === [1, 2, 3] // false
{ username: 'hitmands' } === { username: 'hitmands' } // false
to better understand it, please have a look at MDN - Equality Comparisons and Sameness
how to force immutability?
By always returning a new instance of the given object!
Let's say we have to set the property status of a todo. In the old way we would just do:
todo.status = 'new status';
but, we could force immutability by simply copying the given object and returning a new one.
const todo = { id: 'foo', status: 'pending' };
const newTodo = Object.assign({}, todo, { status: 'completed' });
todo === newTodo // false;
todo.status // 'pending'
newTodo.status // 'completed'
coming back to your example, instead of doing obj.count = ..., we would just do:
Object.assign({}, obj, { count: ... })
// or
({ ...obj, count: /* something */ })
there are libraries that help you with the immutable pattern:
Immer
ImmutableJS
You use the freeze method supplying the object you want to make immutable.
const person = { name: "Bob", age: 26 }
Object.freeze(person)
You could use the new ES6 built-in immutability mechanisms, or you could just wrap a nice getter around your objects similar to this
var myProvider = {}
function (context)
{
function initializeMyObject()
{
return 5;
}
var myImmutableObject = initializeMyObject();
context.getImmutableObject = function()
{
// make an in-depth copy of your object.
var x = myImmutableObject
return x;
}
}(myProvider);
var x = myProvider.getImmutableObject();
This will keep your object enclosed outside of global scope, but the getter will be accessible in your global scope.
You can read more on this coding pattern here
One easy way to make "copies" of mutable objects is to stringify them into another object and then parse them back into a new array. This works for me.
function returnCopy(arrayThing) {
let str = JSON.stringify(arrayThing);
let arr = JSON.parse(str);
return arr;
}
Actually, you can use spread opreator to make the original array stay unchanged, below y is the immutable array example:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = [...y]; // make array y as immutable array [1,2,3,4,5]
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
If you don't use the spread operator, the y array will get changed when loop is running time by time.
Here is the mutable array result:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = y; // this is mutable, because arr and y has same memory address
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
You assign these properties in your map function, you need to change this. (Just declare an empty object instead of using your current obj)
I have a big object array persons
persons = [{name:'john1'}, {name:'john2'},...]
I iterated the array and found the object I am interested to edit
objectToEdit = persons .find((person)=>person.name==='john1')
Now I created an edited object in immutable way (someOtherPerson = {name:'johnxx'})
objectFinal = {...objectToEdit, someOtherPerson}
Now I want to replace this objectFinal with objectToEdit in persons array, without having to traverse the array again. But doing objectToEdit =objectFinal , will just assign objectToEdited's reference to objectToEdit , without making any change in the persons array
Is there a clean way to achieve this without traversing the array?
Edit:
In this example, the object in persons jave just one key (i.e, name). This is to make question minimal. In my project, I have more than 30 keys.
If you want to edit an object in a list,in place, use Array.prototype.some
var persons = [{
name: 'john1'
}, {
name: 'jack5'
}]
var someOtherPerson = {
name: 'johnxx'
}
persons.some(function(person) {
// if condition, edit and return true
if (person.name === 'john1') {
// use Object.keys to copy properties
Object.keys(someOtherPerson).forEach(function(key) {
person[key] = someOtherPerson[key]
})
// or use assign to merge 2 objects
Object.assign(person, someOtherPerson);
return true // stops iteration
}
})
console.log(JSON.stringify(persons))
If you want to avoid mutating the objects in the original array, you might use .findIndex instead, and reassign the item in the array at that index:
const persons = [{name:'john1'}, {name:'john2'}];
const objectToEditIndex = persons.findIndex((person) => person.name === 'john1');
const someOtherPerson = {name:"johnxx"};
persons[objectToEditIndex] = {
...persons[objectToEditIndex],
...someOtherPerson
};
console.log(persons);
Doing this:
objectFinal = {...objectToEdit, someOtherPerson}
you lose the reference to the original objectToEdit object. To edit it, you can do
`objectToEdit.value = otherValue`.
PS: Having said that, you only lose reference to the first level object. If that object, contains another object or array, you have reference to it:
objectFinal.innerObject.value = otherValue;
{name:'john1'} should be replace with {name:"johnxx"} in persons
Reassign persons to the persons-array mapped with the new value:
let persons = [{name:'john1'}, {name:'john2'}];
persons = persons.map( v => v.name === "john1" ? {name: "johnxx"} : v );
console.log(persons);
Or, if you're worried about performance, use Array.splice in combination with Array.findIndex
The findIndex method executes the callback function once for every
array index 0..length-1 (inclusive) in the array until it finds one
where callback returns a truthy value (a value that coerces to true).
If such an element is found, findIndex immediately returns the index
for that iteration.
let persons = [{name:'john1'}, {name:'john2'}];
persons.splice(persons.findIndex( v => v.name==="john1" ), 1, {name:'johnxx'});
console.log(persons);
Or use a utility method (snippet for a large array (1,000,000 elements) and with performance timer)
// replacer method
const replace = (obj, [key, value], replacement) => {
obj.splice(persons.findIndex( v => v[key] === value ), 1, replacement);
return obj;
}
// create a large array (this may take some extra time)
let persons = Array.from({length: 1000000}).map(v =>
({name: Math.floor(100000 + Math.random() * 1000000000).toString(16)}) );
// pick an entry
const someEntry = Math.floor(persons.length * Math.random());
const entry = Object.entries(persons[someEntry])[0];
console.log("before:", persons[someEntry]);
const t = performance.now();
// replacement call here
console.log("after:", replace(persons, entry, {name: "johnxx"})[someEntry]);
console.log("replacement took:", `${((performance.now() - t)/1000).toFixed(3)} sec`);
I have some problem with sorting items inside object. So I have something like this:
var someObject = {
'type1': 'abc',
'type2': 'gty',
'type3': 'qwe',
'type4': 'bbvdd',
'type5': 'zxczvdf'
};
I want to sort someObject by value, and this is where I have problem.
I have sorting function that should return key/value pairs sorted by value:
function SortObject(passedObject) {
var values = [];
var sorted_obj = {};
for (var key in passedObject) {
if (passedObject.hasOwnProperty(key)) {
values.push(passedObject[key]);
}
}
// sort keys
values.sort();
// create new object based on Sorted Keys
jQuery.each(values, function (i, value) {
var key = GetKey(passedObject, value);
sorted_obj[key] = value;
});
return sorted_obj;
}
and function to get key:
function GetKey(someObject, value) {
for (var key in someObject) {
if (someObject[key] === value) {
return key;
}
}
}
The problem is in last part when creating new, returning object - it's sorted by key again. Why? And this is specific situation when i have to operate on object NOT on array (yes I know that would be easier...)
Does anyone know how to sort items in object?
Plain objects don't have order at all. Arrays -that are a special types of objects- have.
The most close thing that you can have is an array with the object values sorted . Something like, for example:
_valuesOfAnObjectSorted = Object.keys(object).map(function(k){ return object[k]; }).sort();
You have two possibilities:
Refactor your object into an array
Something like this:
var myObj = [
['type1', 'abc'],
['type2', 'gty'],
...
];
Or even better, since using it somewhere would not rely on array positions but full named keys:
var myObj = [
{name: 'type1', val:'abc'},
{name: 'type2', val:'gty'},
...
];
Use your object with an auxiliar array
Wherever you want to use your object ordered by the keys, you can extract the keys as an array, order it and traverse it to access the object
var ordKeys = Object.keys(myObj).sort(); // pass inside a function if you want specific order
var key;
for (var i = 0, len = ordKeys.length; i < len; i +=1) {
key = ordKeys[i]
alert(key + " - " + myObj[key]);
}
Combination of both of them
If the object is not constructed by you, but comes from somewhere else, you can use the second option approach to construct an array of objects as in the first option. That would let you use your array anywhere with perfect order.
EDIT
You might want to check the library underscore.js. There you have extremely useful methods that could do the trick pretty easily. Probably the method _.pairs with some mapping would do all the work in one statement.
Why is my for for-each loop not iterating over my JavaScript associative array object?
// Defining an array
var array = [];
// Assigning values to corresponding keys
array["Main"] = "Main page";
array["Guide"] = "Guide page";
array["Articles"] = "Articles page";
array["Forum"] = "Forum board";
// Expected: loop over every item,
// yet it logs only "last" assigned value - "Forum"
for (var i = 0; i < array.length; i++) {
console.log(array[i]);
}
jQuery each() could be helpful: https://api.jquery.com/jQuery.each/
The .length property only tracks properties with numeric indexes (keys). You're using strings for keys.
You can do this:
var arr_jq_TabContents = {}; // no need for an array
arr_jq_TabContents["Main"] = jq_TabContents_Main;
arr_jq_TabContents["Guide"] = jq_TabContents_Guide;
arr_jq_TabContents["Articles"] = jq_TabContents_Articles;
arr_jq_TabContents["Forum"] = jq_TabContents_Forum;
for (var key in arr_jq_TabContents) {
console.log(arr_jq_TabContents[key]);
}
To be safe, it's a good idea in loops like that to make sure that none of the properties are unexpected results of inheritance:
for (var key in arr_jq_TabContents) {
if (arr_jq_TabContents.hasOwnProperty(key))
console.log(arr_jq_TabContents[key]);
}
edit — it's probably a good idea now to note that the Object.keys() function is available on modern browsers and in Node etc. That function returns the "own" keys of an object, as an array:
Object.keys(arr_jq_TabContents).forEach(function(key, index) {
console.log(this[key]);
}, arr_jq_TabContents);
The callback function passed to .forEach() is called with each key and the key's index in the array returned by Object.keys(). It's also passed the array through which the function is iterating, but that array is not really useful to us; we need the original object. That can be accessed directly by name, but (in my opinion) it's a little nicer to pass it explicitly, which is done by passing a second argument to .forEach() — the original object — which will be bound as this inside the callback. (Just saw that this was noted in a comment below.)
This is very simple approach. The advantage is you can get keys as well:
for (var key in array) {
var value = array[key];
console.log(key, value);
}
For ES6:
array.forEach(value => {
console.log(value)
})
For ES6 (if you want the value, index and the array itself):
array.forEach((value, index, self) => {
console.log(value, index, self)
})
If Node.js or the browser support Object.entries(), it can be used as an alternative to using Object.keys() (Pointy's answer).
const h = {
a: 1,
b: 2
};
Object.entries(h).forEach(([key, value]) => console.log(value));
// logs 1, 2
In this example, forEach uses destructuring assignment of an array.
There are some straightforward examples already, but I notice from how you've worded your question that you probably come from a PHP background, and you're expecting JavaScript to work the same way -- it does not. A PHP array is very different from a JavaScript Array.
In PHP, an associative array can do most of what a numerically-indexed array can (the array_* functions work, you can count() it, etc.). You simply create an array and start assigning to string indexes instead of numeric.
In JavaScript, everything is an object (except for primitives: string, numeric, boolean), and arrays are a certain implementation that lets you have numeric indexes. Anything pushed to an array will affect its length, and can be iterated over using Array methods (map, forEach, reduce, filter, find, etc.) However, because everything is an object, you're always free to simply assign properties, because that's something you do to any object. Square-bracket notation is simply another way to access a property, so in your case:
array['Main'] = 'Main Page';
is actually equivalent to:
array.Main = 'Main Page';
From your description, my guess is that you want an 'associative array', but for JavaScript, this is a simple case of using an object as a hashmap. Also, I know it's an example, but avoid non-meaningful names that only describe the variable type (e.g. array), and name based on what it should contain (e.g. pages). Simple objects don't have many good direct ways to iterate, so often we'll turn then into arrays first using Object methods (Object.keys in this case -- there's also entries and values being added to some browsers right now) which we can loop.
// Assigning values to corresponding keys
const pages = {
Main: 'Main page',
Guide: 'Guide page',
Articles: 'Articles page',
Forum: 'Forum board',
};
Object.keys(pages).forEach((page) => console.log(page));
arr_jq_TabContents[key] sees the array as an 0-index form.
Here is a simple way to use an associative array as a generic Object type:
Object.prototype.forEach = function(cb){
if(this instanceof Array) return this.forEach(cb);
let self = this;
Object.getOwnPropertyNames(this).forEach(
(k)=>{ cb.call(self, self[k], k); }
);
};
Object({a:1,b:2,c:3}).forEach((value, key)=>{
console.log(`key/value pair: ${key}/${value}`);
});
This is (essentially) incorrect in most cases:
var array = [];
array["Main"] = "Main page";
That creates a non-element property on the array with the name Main. Although arrays are objects, normally you don't want to create non-element properties on them.
If you want to index into array by those names, typically you'd use a Map or a plain object, not an array.
With a Map (ES2015+), which I'll call map because I'm creative:
let map = new Map();
map.set("Main", "Main page");
you then iterate it using the iterators from its values, keys, or entries methods, for instance:
for (const value of map.values()) {
// Here, `value` will be `"Main page"`, etc.
}
Using a plain object, which I'll creatively call obj:
let obj = Object.create(null); // Creates an object with no prototype
obj.Main = "Main page"; // Or: `obj["Main"] = "Main page";`
you'd then iterate its contents using Object.keys, Object.values, or Object.entries, for instance:
for (const value of Object.values(proches_X)) {
// Here, `value` will be `"Main page"`, etc.
}
var obj = {
no: ["no", 32],
nt: ["no", 32],
nf: ["no", 32, 90]
};
count = -1; // Which must be a static value
for (i in obj) {
count++;
if (obj.hasOwnProperty(i)) {
console.log(obj[i][count])
};
};
In this code I used the brackets method for call values in an array because it contained an array. However, briefly the idea which a variable i has a key of property and with a loop called both values of the associative array.
It is the perfect method.
You can do this:
var array = [];
// Assigning values to corresponding keys
array[0] = "Main page";
array[1] = "Guide page";
array[2] = "Articles page";
array[3] = "Forum board";
array.forEach(value => {
console.log(value)
})
It seems like almost every answer is not what was asked at the very first place.
It's seems bit off that foreach-loop does not work. and simple for-loop will not work as well because length property will be zero in case of associative arrays(one of the fallback). but for-in do the thing for associative array
// Defining an array
var array = [];
// Assigning values to corresponding keys
array["Main"] = "Main page";
array["Guide"] = "Guide page";
array["Articles"] = "Articles page";
array["Forum"] = "Forum board";
// Expected: loop over every item,
// yet it logs only "last" assigned value - "Forum"
for (var index in array) {
console.log(index,array[index]);
}