Can I combine spread and rest in destructuring object in Javascript? - javascript

Let us have this object:
// Values of properties are irelevant
let row = {_x: "x", _y: "y", _z: "z", a: "a", b: "b"}
I need to get copy of this object without properties beginning with underscore (_).
I can do this:
const {_x, _y, _z, ...pureRow} = row;
console.log(pureRow); // {a: "a", b: "b"}
But I would like to have a list of removed properties in an array and remove all properties listed in this array. Something like:
const auxFields = ["_x", "_y", "_z"];
const {...auxFields, ...pureRow} = row; // Error: A rest element must be last in a destructuring pattern.
console.log(pureRow); // {a: "a", b: "b"}
Is there some way to achieve this?

Related

How to best create an array of objects from a objects in one array that are not included in a second array [duplicate]

This question already has answers here:
JavaScript get elements from an object array that are not in another
(5 answers)
Closed 3 years ago.
I want an array that contains objects from the scrape object that are not present in the old object. The arrays I'm actually working with contains nearly 100 objects.
The code below works, but I wonder if there's a more efficient way of getting the same result?
var old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
var scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
var nogood = []
var good =[]
scrape.forEach(es => {
old.forEach(e => {
if(e.a == es.a) {
nogood.push(es)
}
})
})
console.log(nogood)
nogood.forEach(main =>
good = scrape.filter(e=> e.a!=main.a)
)
console.log(good)
This is what I expect and what I'm getting:
good = {a:5, b:5}
Personally I would approach this with:
const old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
];
const scrape = [{a: 1, b: 1}, {a: 5, b:5}];
for (const item of old) {
for (const i in scrape) {
if (JSON.stringify(item) === JSON.stringify(scrape[i])) {
scrape.splice(i, 1); //delete the previously scraped item
}
}
}
console.log(scrape); //{a: 5, b:5}
The benefits to this approach are:
You don't care what properties the objects you're comparing have,
you just care about whether they're identical.
It's fast
(comparing JSON is generally faster than traversing the objects to
compare each property).
It's more succinct to splice the scrape
array rather than adding the 'good' and 'nogood' arrays to arrive at
a filtered scrape array.
Possible deal breaker is if the objects you're comparing contain methods, in which case comparing them via JSON is not the correct approach.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
If we have arrays old and scrape to be of size M and N, respectively, all traditional approaches has the complexity of O(M * N) because you need to compare each entry within array scrape with the ones exists in array old to find out whether matches or not.
The second and more efficient approach is to create a hash table on first array, typically on bigger one (old here), and iterate over the second one (scrape here) which has the complexity of O(M + N).
If the size of M and N be as big as enough, the differences show themselves. As an example if M=100 and N=200, the former one needs to compare 20000 objects but the later one needs just 300 comparisons.
please take a look at this code:
const old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
const scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
// create hash map using built-in Javascript Map
const pair = old.map(x => [JSON.stringify(x), true])
const map = new Map(pair)
// filter those object does not exist in hash map
const good = scrape.filter(x => !map.has(JSON.stringify(x)))
console.log(good)
How about something like this?
const good = scrape.filter((sEl) => {
return !old.some(oEl => oEl.a === sEl.a);
})
This avoids the nested forEach loops and .some will return as soon as a single true condition is found, avoiding some excess searching when an element exists early in the 'old' array.
May be something like:
var old = [
{a: 6, b: 3},
{a: 1, b: 1},
{a: 3, b: 3}
]
var scrape = [
{a: 1, b: 1},
{a: 5, b:5}
]
var result = scrape.filter(s => old.findIndex(o => o.a === s.a) === -1);
console.log(result);

How to get the key for a array with his value is equal True

I'm trying to get all the keys on my Object with a value "T" (true) and show them in the component but i'm getting troubles.
I tried with for and forEach but I can't get the keys.
This is my render method and this is the object
render(){
const races = this.state.data.racesTrack.Races;
const racesList = [];
}
I need to see it like this
exacta
hq
place:
quinella
show:
spr:
trifecta:
wps:
Image with the object: https://i.stack.imgur.com/en09V.png
You can do something like this in ES6:
var raceList = []
var races = {
a: "T",
b: "F",
c: "T",
d: "F"
}
for (key in races) {
if (races[key] == "T") {
raceList.push(key)
}
}
console.log(raceList)
The problem you are asking is not related to Reactjs and is about the basics of javascript.
A simpler solution can be using Object Keys Array and the Filter method:
var race = {
a: "T",
b: "F",
c: "T",
d: "F"
}
var sort = Object.keys(race).filter((key)=>{
return race[key]==='T'
})
console.log(sort)

Destructuring for get the first property of an object?

For arrays, we can define the properties depending on it's indexes like:
const arr = [1, 2, 3];
const [first, second, third] = arr;
console.log(first, second, third)
I'm just wondering if there's a possible solution to do it's reverse with objects like:
const obj = {first: "a", second: "b", third: "c"}
const {0, 1, 2} = obj;
//expected: "a", "b", "c"
You do it like this for objects:
const obj = {foo: 123, bar: 'str'}
const {foo, bar} = obj
It isn't.
Objects are not designed to be ordered, so there isn't a first property per se.
You could convert an object into an array of its values first …
const obj = {
first: "a",
second: "b",
third: "c"
}
const array = Object.values(obj);
const [foo, bar, baz] = array;
console.log({
foo,
bar,
baz
});
… but it is unlikely to be useful and it certainly wouldn't be intuitive code that is easy to maintain.
Try this:
const obj = {first: "a", second: "b", third: "c"}
const indexes = [0, 1, 2]
indexes.map( (val) => { return Object.values(obj)[val] } ) //["a", "b", "c"]
You could take the values and assign this to an array for destructuring.
The order is actually dtermined by the insertation order or if a key is like a valid index of an array, it is sorted numerically to top.
const
object = { first: "a", second: "b", third: "c" },
[first, second, third] = Object.values(object);
console.log(first, second, third);
For extracting a an arbitrary position, you vould take an object with an index an object property assignment pattern [YDKJS: ES6 & Beyond] for a new valid variable.
const
object = { first: "a", second: "b", third: "c" },
{ 2: foo } = Object.values(object);
console.log(foo);

Why using toArray filter with ng-repeat and an object of strings causes an infinite digest loop?

I would like to use the orderBy filter with the ng-repeat directive while iterating over an objects properties.
As the orderBy filter only works with Array, the Angular doc suggests to use the toArray filter.
The toArray filter works like a charm with object-properties like:
var obj = {
a: { name: 'A' },
b: { name: 'B' },
c: { name: 'C' }
};
But it causes an infinite digest loop when used with non-object properties:
var obj = {
a: 'A',
b: 'B',
c: 'C'
};
Here is a plunker illustrating the issue.
You should not do that anyway, filters are generally bad idea when transforming data, cos it will get recalculated every time digest cycle makes a loop. Your plunker does not work, so it is hard to say why, but looking at code I would say that it does make completly new array with each digest loop, and in case of objects it adds $key property, which helps stoping digest cycle. It cannot add such property to strings. But I'm not really sure about that.
Edit:
when you add console.log to toArray:
return Object.keys(obj).map(function (key) {
var value = obj[key];
console.log(key, value);
return angular.isObject(value) ?
Object.defineProperty(value, '$key', { enumerable: false, value: key}) :
{ $key: key, $value: value };
});
In logs you can see answer to your question:
VM596 angular-toArrayFilter.js:15 b b
VM596 angular-toArrayFilter.js:15 a a
VM596 angular-toArrayFilter.js:15 c Object {p: "c"}
VM596 angular-toArrayFilter.js:15 b Object {p: "b"}
VM596 angular-toArrayFilter.js:15 a Object {p: "a"}
VM596 angular-toArrayFilter.js:15 c Object {p: "c", $key: "c"}
VM596 angular-toArrayFilter.js:15 b Object {p: "b", $key: "b"}
VM596 angular-toArrayFilter.js:15 a Object {p: "a", $key: "a"}
VM596 angular-toArrayFilter.js:15 c c
VM596 angular-toArrayFilter.js:15 b b
VM596 angular-toArrayFilter.js:15 a a
VM596 angular-toArrayFilter.js:15 c Object {p: "c", $$hashKey: "object:11", $key: "c"}
VM596 angular-toArrayFilter.js:15 b Object {p: "b", $$hashKey: "object:10", $key: "b"}
VM596 angular-toArrayFilter.js:15 a Object {p: "a", $$hashKey: "object:9", $key: "a"}
In case objects, angular is using the same objects and does not create new ones. So that it can assume that array did not change and end digest cycle. In case of string values it creates new objects each time filter is run, so that it assumes that each time different array is created, so that it cannot end digest cycle.

jQuery map selected object

I am working with JavaScript, could you help me please
Here is my problem.
I have this object:
var MyObj= [{ a: 0, b: "Zero", c: "x", d: "!" }, { a: 1, b: "One", c: "y", d: "#" }]
I want to change the element of selected object ("a" --> "id") to become like this:
var NewObj= [{ id: 0, b: "Zero", c: "x", d: "!" }, { id: 1, b: "One", c: "y", d: "#" }]
I tried to use $.map() method like this
NewObj= $.map(MyObj, function (obj) {
return { id: obj.a, b: obj.b, c: obj.c, d:obj.d };
});
Is there any better way to do this since I only change one element of object?
No need for ES6 / Object.assign, no need for jQuery:
Working Fiddle: https://jsbin.com/gosaqid/edit?js,console
function makeObj(obj){
return obj.map(function(el, i) {
el.id = i;
delete el.a;
return el;
});
}
Not unless you have a clone/copy/extend function available. One is coming up in new JavaScript, and jQuery has one, and it's not very hard writing your own. But it still isn't a walk in the park - you can't just rename a property, you need to copy and delete:
NewObj = MyObj.map(function(obj) {
var newobj = Object.assign({}, obj, {id: obj.a});
delete newobj.a;
return newobj;
});
In your example MyObj is an array of objects.
var object = {}
var array = []
var arrayOfObjects = [{}, {}, {}]
In your desired result, you have changed one of the keys of every object in the array.
Using map is a perfectly adequate way of doing this, in fact JavaScript's array has a built in map method.
var newArrayOfObjects = arrayOfObjects.map(function (obj) {
return {
id: obj.a,
b: obj.b,
c: obj.c
}
})
If you have a ton of keys this can get a little verbose so you can use $.extend, but chances are you're writing code for modern browsers so the whole thing can be written as such:
var newArrayOfObjects = arrayOfObjects.map(obj =>
Object.assign({}, obj, { id: obj.a })
)
update: as #Amadan suggests, you can also delete the old key if you need

Categories

Resources