This might be a noob question for you guys but I just want to clarify something. I'm new to JS and I know that arrays are just objects with fields as indexes.
I have some lines of code here. The objective is pretty easy, is to pass a parameter function to an array and map it to another one.
My confusion is that the _obj is declared as an object with _obj = {}, and we have to do _obj[obj.key] = obj.value to map the keys and values. What is actually going around here?
It makes me feel like there are two nested arrays and it feels gross. I hope you understand me and I just need to know if there is another way or what actually is going on.
Stay at home guys!
Thanks in advance.
const objArray = [
{key:1, value:10},
{key:2, value:20},
{key:3, value:30},
{key:4, value:40}
];
const newArray = objArray.map(obj => {
let _obj = {};
_obj[obj.key] = obj.value;
return _obj;
});
console.log(newArray);
//Array [Object { 1: 10 }, Object { 2: 20 }, Object { 3: 30 }, Object { 4: 40 }]
I just need to know if there is another way or what actually is going
on.
When you do:
_obj[obj.key] = obj.value;
... you are setting a key/property on the object _obj to hold a particular value. In this case, the key you are setting is the value of obj.key and the value is obj.value. The values of obj.key and obj.value changes depending on what object you are iterated on in the .map() callback. For example, if you are looking at the first object in your array, obj.key would be 1 and obj.value would be 10. Thus, doing _obj[obj.key] = obj.value would be equivalent to _obj[1] = 10, which sets the key of 1 to have the value of 10 on the _obj object:
{
1: 10
}
This process occurs for each object in your array.
As for a different approach, you could use computed-property names introduced in ES6 with destructuring assignment to return an object literal with the key property as the key for the new object literal and the value property and the actual value for the new object:
const objArray = [
{key:1, value:10},
{key:2, value:20},
{key:3, value:30},
{key:4, value:40}
];
const newArray = objArray.map(({key, value}) => ({[key]: value}));
console.log(newArray);
In javascript, just like java, everything that is not a primitive is an object. Arrays are actually an object that just have some utility functions added in, such as the length attribute, and .map. To access a property, you can either do obj.prop, or use an arbitrary string like python's dictionaries like obj["prop"].
Array is special object, In which all key should be Number. The array has some pre-defined method, Which is made to iterate and perform similar operations. The array is linear and implements stack implementation. Array element could be any other object or Array.
To understand:
const object = {
1: "something",
2: "something2"
}
const object2 = {
"1_1": "something",
"2_1": "something2"
}
// console.log(object[1])
// console.log(object[2])
// console.log(object2["2_1"]) // Key string
// console.log(object2["1_1"])
// object.forEach(console.log) // Error
const array = ["something", "something2"]
const array2 = []
array2["1_1"] = "something"
array2["2_1"] = "something2"
console.log(array[0]) // index start with 0, default
console.log(array[1])
console.log(array2["2_1"]) // Key string
console.log(array2["1_1"]) // Works
array.forEach(console.log) // No error, something 0 [ 'something', 'something2' ] something2 1 [ 'something', 'something2' ]
array2.forEach(console.log) // No error, // No output
Here is the variant with reduce method:
const objArray = [
{key:1, value:10},
{key:2, value:20},
{key:3, value:30},
{key:4, value:40}
];
const newArray = objArray.reduce((acc, rec) => { return {...acc, [rec.key]: rec.value} }, []);
console.log(JSON.stringify(newArray))
// {"1":10,"2":20,"3":30,"4":40}
Related
In its most basic form, having an array of objects:
let arr = [
{val:"a"},
{val:"b"}
];
How can destructuring be used, to obtain only the values ['a', 'b'].
getting the first value is easy:
let [{val:res}] = arr; //res contains 'a'
Obtaining all values inside the array can be done with the rest operator:
let [...res] = arr; //res contains all objects
Combining those, I expected to be able to use:
let [...{val:res}] = arr; //undefined, expected all 'val's (['a', 'b'])
The above returns undefined (Tested in FF). Some further testing seems to indicate that adding the rest operator when using an object destructuring as well doesn't use the iteration, but gets back the original object, e.g. let [...{length:res}] = arr; //res= 2. Some other trials, such as let [{val:...res}] = arr; or let [{val}:...res] = arr; produce syntax errors.
It's easy enough to do with other methods, such as using map on the array, but mostly I stumble upon this problem while destructuring multiple levels (an array with objects which have their own property containing an array). Therefore I'm really trying to get around how to do it solely with destructuring.
For convenience: a test fiddle
edit
My apologies if I failed to explain the goal of the question. I'm not looking for a solution to a specific problem, only to find the correct syntax to use when destructuring.
Otherwise formulated, a first question would be: in the example above, why doesn't let [...{val:res}] = arr; return all values (['a', 'b']). The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here). It seems that the latter is not supported, but I haven't come across any documentation that (and why) it wouldn't be.
Why doesn't let [...{val:res}] = arr; return all values (['a', 'b'])?
You seem to confuse the rest syntax with array comprehensions.
If you assign a value to [someElements, ...someExpression], the value is tested to be iterable and then each element generated by the iterator is assigned to the respective someElements variable. If you use the rest syntax in the destructuring expression, an array is created and the iterator is ran till its end while filling the array with the generated values. Then that array is assigned to the someExpression.
All of these assignment targets can be other destructuring expressions (arbitrarily nested and recursively evaluated), or references to variable or properties.
So if you do let [...{val:res}] = arr, it will create an array and fill that with all the values from the iterator of arr:
let {val:res} = Array.from(arr[Symbol.iterator]())
You can see now why that ends up with undefined, and why using something like [...{length:res}] does yield a result. Another example:
let [{val:res1}, ...{length: res2}] = arr;
console.log(res1) // 'a'
console.log(res2) // 1 (length of `[{val: 'b'}]`)
How can destructuring be used to obtain only the values ['a', 'b']?
Not at all. Use the map method.
You can destructure nested objects like this
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Nested_object_and_array_destructuring
let arr = [
{val:"a"},
{val:"b"}
];
const [{val: valueOfA}, {val: valueOfB}] = arr
console.log(
valueOfA, valueOfB
)
Beside mapping with a callback for the value
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(o => o.val));
you could use deconstructiong inside of the paramter list and use only the value to return.
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(({val}) => val));
At this point of time you can use both For of loop with ES6 Object destructuring.
let arr = [{val:"a"},{val:"b"}];
for (const item in arr){
const {val} = arr[item];
console.log(val);
}
You can declare assignment target before destructuring assignment; at destructuring target, set values of assignments target indexes by from destructuring source
let arr1 = [{val: "a"}, {val: "b"}];
let arr2 = [{"foo":1,"arr":[{"val":"a"},{"val":"b"}]}
, {"foo":2,"arr":[{"val":"c"},{"val":"d"}]}];
let [res1, res2] = [[], []];
[{val: res1[0]}, {val: res1[1]}] = arr1;
[{arr: [{val:res2[0]}, {val:res2[1]}]}
, {arr: [{val:res2[2]}, {val:res2[3]}]}] = arr2;
console.log(res1, res2);
You can alternatively use rest element at target to collect values at source by including comma operator following object pattern to return value pulled from object
let arr = [{val: "a"}, {val: "b"}];
let [...res] = [({val} = arr[0], val), ({val} = arr[1], val)];
console.log(res)
I am using DataTables library and I have hard times in receiving data in a proper format so I am trying to adjust it before DataTable library tries to fetch data into table. I have an ajax call which returns an object of the following format:
data:[ [{ Key: "SomeKey" , Value: "SomeValue" } , { ...} ],[...] ]
And my desired output is: data:[ [{ "SomeKey":"SomeValue" } , { ...} ],[...] ]
I have tried JSON.stringify or eval method , but did not worked , also tried those 2 methods when return type was some sort of string but then it inserts \ before " so It does not convert to json. Any help or good tracks would be appreciated.
This has nothing to do with JSON. :-)
data is apparently an array of arrays of objects, where each object has properties valled Key and Value.
If you want to create a new array of arrays of objects, where the objects have a property named by the Key value whose value is the Value value, you can do that like this:
data = data.map(a => a.map(({Key,Value}) => ({[Key]: Value})));
That uses map on the arrays (both the outer and inner ones) and destructuring to pick out the Key and Value properties from each object in the subarrays, and uses computed property names to set the property name on the new object.
In ES5 and earlier, that would look like this:
data = data.map(function(a) {
return a.map(function(obj) {
var newObj = {};
newObj[obj.Key] = obj.Value;
return newObj;
});
});
You should look into Array.prototype.map (mdn)
let data = [[{Key: "SomeKey", Value: "SomeValue"}]];
let output = data.map(a => a.map(({Key, Value}) => ({[Key]: Value})));
console.log(output);
Note the [Key] syntax. To put it simply, whereas var x = 'key'; y = {x: 3} will assign the object {x: 3}, x = 'key'; y = {[x]: 3} will assign the object {key: 3}.
If you're receiving literally the string "data:[ [{ Key: "SomeKey" , Value: "SomeValue" } , { ...} ],[...] ]", then you may trim the first 5 characters ('data:') and then use JSON.parse.
Hi I have following objects in an array and they appear like this:
[{col1:abc}
{col2:def}
{col1:ghi}]
What I want to do is if the same key is coming again I should overwrite it so it becomes
[{col1:ghi}
{col2:def}]
instead of appending another key value pair.
I am thinking of something like to overwrite
[col1:{col1:ghi}
col2:{col2:def}]
so that i can easily iterate over them in future.
Is there any way to put my keys in this way by using map or something similar?
Thank you
I'm going to assume all your objects look like the ones you provided in your question, i.e. they each are a single key-value pair (like {col1: "abc"})
We first need to loop over all the objects in your array, combining them into one large object. Since an object cannot have two identical keys, this has the effect of overwriting values associated with a key that occur earlier in the array with ones that associate with the same key, but occur later. This can be achieved with:
const unifiedObj = arr.reduce((acc, obj) => Object.assign(acc, obj), {})
reduce is a way of "looping" over the items in an array (well, not exactly, but you can think of it this way for now). Object.assign is a way to merge two objects. You should look these up in the MDN docs.
So now, if your original array looked like this:
[
{col1:"abc"}
{col2:"def"}
{col1:"ghi"}
]
The "unified" object will look like this:
{
col1: "ghi",
col2: "def"
}
Next, since you want an array of 'single key-value pair objects' as your final result, instead of this unified object, we're going to have to extract each key-value pair in the unified object into a new object, and collect all those new objects into an array. That's what this statement does.
const result = Object.keys(unifiedObj).map(k => ({k: unifiedObj[k]}))
Object.keys gives you all the keys of an object as an array. map transforms an array into another array, using the function supplied as its argument. Look these up too.
At the end, result will be an array that looks like this:
[
{ col1: "ghi" },
{ col2: "def" }
]
which seems to be what you wanted. Do note that the objects in the array might be in a different order from what you expect, i.e. the final array may also look like this:
[
{ col2: "def" },
{ col1: "ghi" }
]
This is quite an easy task! The explanation is in the code comments.
const arr = [{col1:'abc'},
{col2:'def'},
{col1:'ghi'}]
arr.forEach((item, index) => {
// get the key `col1`, `col2` etc. (only works if there is one key in the object!)
const key = Object.keys(item)[0]
// now check if the key was previously encountered
for (i = 0; i < index; i++) {
if (arr[i][key] !== undefined) {
// the same key was found in the already processed chunk of the array! Rewrite it with the latter value!
arr[i][key] = item
}
}
})
console.log(arr)
I think you probably want to produce a single object from your array of objects and retain all values for the same key, so that
[
{col1: 'abc'},
{col2: 'def'},
{col1: 'ghi'}
]
becomes
{
col1: ['abc', 'ghi'],
col2: ['def']
}
if that sounds right, then here's how to do it:
const arr = [
{col1: 'abc'},
{col2: 'def'},
{col1: 'ghi'}
];
const result = arr.reduce((memo, o) => {
Object.entries(o).forEach(([key, val]) => {
memo[key] = memo[key] || [];
memo[key].push(val);
});
return memo;
}, {});
console.log(result)
In its most basic form, having an array of objects:
let arr = [
{val:"a"},
{val:"b"}
];
How can destructuring be used, to obtain only the values ['a', 'b'].
getting the first value is easy:
let [{val:res}] = arr; //res contains 'a'
Obtaining all values inside the array can be done with the rest operator:
let [...res] = arr; //res contains all objects
Combining those, I expected to be able to use:
let [...{val:res}] = arr; //undefined, expected all 'val's (['a', 'b'])
The above returns undefined (Tested in FF). Some further testing seems to indicate that adding the rest operator when using an object destructuring as well doesn't use the iteration, but gets back the original object, e.g. let [...{length:res}] = arr; //res= 2. Some other trials, such as let [{val:...res}] = arr; or let [{val}:...res] = arr; produce syntax errors.
It's easy enough to do with other methods, such as using map on the array, but mostly I stumble upon this problem while destructuring multiple levels (an array with objects which have their own property containing an array). Therefore I'm really trying to get around how to do it solely with destructuring.
For convenience: a test fiddle
edit
My apologies if I failed to explain the goal of the question. I'm not looking for a solution to a specific problem, only to find the correct syntax to use when destructuring.
Otherwise formulated, a first question would be: in the example above, why doesn't let [...{val:res}] = arr; return all values (['a', 'b']). The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here). It seems that the latter is not supported, but I haven't come across any documentation that (and why) it wouldn't be.
Why doesn't let [...{val:res}] = arr; return all values (['a', 'b'])?
You seem to confuse the rest syntax with array comprehensions.
If you assign a value to [someElements, ...someExpression], the value is tested to be iterable and then each element generated by the iterator is assigned to the respective someElements variable. If you use the rest syntax in the destructuring expression, an array is created and the iterator is ran till its end while filling the array with the generated values. Then that array is assigned to the someExpression.
All of these assignment targets can be other destructuring expressions (arbitrarily nested and recursively evaluated), or references to variable or properties.
So if you do let [...{val:res}] = arr, it will create an array and fill that with all the values from the iterator of arr:
let {val:res} = Array.from(arr[Symbol.iterator]())
You can see now why that ends up with undefined, and why using something like [...{length:res}] does yield a result. Another example:
let [{val:res1}, ...{length: res2}] = arr;
console.log(res1) // 'a'
console.log(res2) // 1 (length of `[{val: 'b'}]`)
How can destructuring be used to obtain only the values ['a', 'b']?
Not at all. Use the map method.
You can destructure nested objects like this
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Nested_object_and_array_destructuring
let arr = [
{val:"a"},
{val:"b"}
];
const [{val: valueOfA}, {val: valueOfB}] = arr
console.log(
valueOfA, valueOfB
)
Beside mapping with a callback for the value
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(o => o.val));
you could use deconstructiong inside of the paramter list and use only the value to return.
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(({val}) => val));
At this point of time you can use both For of loop with ES6 Object destructuring.
let arr = [{val:"a"},{val:"b"}];
for (const item in arr){
const {val} = arr[item];
console.log(val);
}
You can declare assignment target before destructuring assignment; at destructuring target, set values of assignments target indexes by from destructuring source
let arr1 = [{val: "a"}, {val: "b"}];
let arr2 = [{"foo":1,"arr":[{"val":"a"},{"val":"b"}]}
, {"foo":2,"arr":[{"val":"c"},{"val":"d"}]}];
let [res1, res2] = [[], []];
[{val: res1[0]}, {val: res1[1]}] = arr1;
[{arr: [{val:res2[0]}, {val:res2[1]}]}
, {arr: [{val:res2[2]}, {val:res2[3]}]}] = arr2;
console.log(res1, res2);
You can alternatively use rest element at target to collect values at source by including comma operator following object pattern to return value pulled from object
let arr = [{val: "a"}, {val: "b"}];
let [...res] = [({val} = arr[0], val), ({val} = arr[1], val)];
console.log(res)
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;
}
));