How to find dynamically a key values inside an object? - javascript

each API request I'm making contains different keys values inside a specific object.
How can I dynamically get the Number value of the second key? ("123112042")
"salesRanks": {
"281052": [ keepaTime, salesRank, ... ]
"123112042": [ keepaTime, salesRank, ... ]
}

Target the Object.keys and get the second index.
const data = {
salesRanks: {
"281052": [1, 1],
"123112042": [2, 2]
}
};
console.log(Object.keys(data.salesRanks)[1]);

Although an object's keys are not really meant to be ordered, you could write an object iterator and assign with destructuring like this:
const obj = {
salesRanks: {
281052: ["keepaTime", "salesRank"],
123112042: ["keepaTime", "salesRank"],
[Symbol.iterator]: function () {
return Object.keys(this).values();
}
},
};
const {
salesRanks: [, second],
} = obj;
console.log(second);

Related

Can destructuring an array be used to map properties of each elements?

Suppose there is an array like this:
const a = [ {p:1}, {p:2}, {p:3} ];
Is it possible to destructure this array in order to obtain p = [1, 2, 3] ?
Because this does not work :
const [ ...{ p } ] = a; // no error, same as const p = a.p;
// p = undefined;
Edit
In response to all the answers saying that I need to use Array.prototype.map, I am aware of this. I was simply wondering if there was a way to map during the destructuring process, and the answer is : no, I need to destructure the array itself, then use map as a separate step.
For example:
const data = {
id: 123,
name: 'John',
attributes: [{ id:300, label:'attrA' }, { id:301, label:'attrB' }]
};
function format(data) {
const { id, name, attributes } = data;
const attr = attributes.map(({ label }) => label);
return { id, name, attr };
}
console.log( format(data) };
// { id:123, name:'John', attr:['attrA', 'attrB'] }
I was simply wondering if there was a way, directly during destructuring, without using map (and, respectfully, without the bloated lodash library), to retrive all label properties into an array of strings.
Honestly I think that what you are looking for doesn't exist, normally you would map the array to create a new array using values from properties. In this specific case it would be like this
const p = a.map(element => element.p)
Of course, there are some packages that have many utilities to help, like Lodash's map function with the 'property' iteratee
you can destructure the first item like this :
const [{ p }] = a;
but for getting all values you need to use .map
and the simplest way might be this :
const val = a.map(({p}) => p)
Here's a generalized solution that groups all properties into arrays, letting you destructure any property:
const group = (array) => array.reduce((acc,obj) => {
for(let [key,val] of Object.entries(obj)){
acc[key] ||= [];
acc[key].push(val)
}
return acc
}, {})
const ar = [ {p:1}, {p:2}, {p:3} ];
const {p} = group(ar)
console.log(p)
const ar2 = [{a:2,b:1},{a:5,b:4}, {c:1}]
const {a,b,c} = group(ar2)
console.log(a,b,c)

checking if an object contains any values besides expected ones

I want to check if an object has a value besides pageSize & pageStart & page , these values arent always all there for example sometimes pagestart will be missing if it equals 0. So checking the length of the object isnt reliable.
let object = {
pageStart: 50,
page: 2
userName: "Bobby"
}
I want to check if the object contains anything besides these values and return a true/false.
One concise option would be to destructure those properties out, use rest syntax to put the rest of the properties into their own object, then see if that object contains any properties.
const { pageSize, pageStart, page, ...rest } = object;
return Object.keys(rest).length >= 1;
You can use some() testing if any of the Object.keys are not included in an array of expected keys. Here using Set.has() for efficient repeat polling. some() will short-circuit on the first match.
const hasUnexpectedKeys = (obj, expected) => {
const expectedSet = new Set(expected);
return Object.keys(obj).some(k => !expectedSet.has(k));
}
const expected = ['pageSize', 'pageStart', 'page'];
console.log(
hasUnexpectedKeys({ pageStart: 50, page: 2, userName: "Bobby" }, expected)
); // true
console.log(
hasUnexpectedKeys({ pageSize: 4, pageStart: 50, page: 2 }, expected)
); // false
Alternatively with just Array.includes()
const
expected = ['pageSize', 'pageStart', 'page'],
object = { pageStart: 50, page: 2, userName: "Bobby" };
console.log(
Object.keys(object).some(k => !expected.includes(k))
);
Iterate all keys of object (or some) and if you find something not in the whitelist, then raise a flag.
var allowed = "pageStart,page,userName".split(",")
var obj1 = {
page: 2,
pageStart: 2,
hello: 'world'
}
var obj2 = {
page: 2,
pageStart: 2
}
function find_alien(obj) {
for (var key in obj) {
if (allowed.indexOf(key) === -1) {
return key;
}
}
return false;
}
console.log(find_alien(obj1))
console.log(find_alien(obj2))

Flatten inner array but assign outer properties

I have an input like this:
var input = [
{
"inner_array": [
{
"inner_data": "inner_data1"
},
{
"inner_data": "inner_data2"
}
],
"outer_data": "outer_data",
}
];
And I'd like to process it so it becomes this.
var output = [
{
"inner_data": "inner_data1",
"outer_data": "outer_data",
},
{
"inner_data": "inner_data2",
"outer_data": "outer_data",
}
];
In words: I'd like to flatten an inner array, while still keeping the outer array's properties. Does this have an easy solution (with built in lambda array functions), or should I write a function myself which handles this?
You could use .flatMap() on your input array to loop over each object, along with an inner .map() to map each inner_array to a new object. The new object can be a combination of the outer_data value along with the inner_data value. The .flatMap() method will then merge all returned objects from the inner .map() calls for each object within input into one resulting array:
const input = [{ "inner_array": [{ "inner_data": "inner_data1" }, { "inner_data": "inner_data2" } ], "outer_data": "outer_data", }];
const res = input.flatMap(obj => obj.inner_array.map(inner => ({
...inner,
outer_data: obj.outer_data
})));
console.log(res);

javascript recursive function concat not working

I have a nested array of objects like below and I'm trying to push all the values in to a single array. all the values are located in sp->it->value or sp->it->it->value
[
{
"sp": [
{
"it":[
{"value":5}
]
},
...
],
"b": {
...
}
},
{
"sp": [
{
"it":[
{"nm":5}
]
}
],
"b": {
...
}
},
{
"sp": [
{
"it":[
{
"it":[
{"value":5}
]
}
]
}
],
"b": {
...
}
},
]
and here is what I have tried
const getValues = (js) => {
let values = []
js.map((val,i) => {
if("sp" in val) values.concat(getValues(val.sp))
else if("it" in val) values.concat(getValues(val.it))
else if("value" in val) values.push(val.value)
})
return values
}
I thought I could concatenate the returned value from the recursive call since it returns an array but the above code returns empty array. Any insights?
Edit fixed the typo on sp object. It is array of objects.
Array.prototype.concat()
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
So those lines do nothing:
if("sp" in val) values.concat(getValues(val.sp))
else if("it" in val) values.concat(getValues(val.it))
You need to write:
if("sp" in val) values = values.concat(getValues(val.sp))
else if("it" in val) values = values.concat(getValues(val.it))
And you should not use map if you don't use it's result. Use forEach instead.
This is because you are passing val.sp to function which is not array but it is an object and .map is a property of an array

Creating a JavaScript function that filters out duplicate in-memory objects?

Okay, so I am trying to create a function that allows you to input an array of Objects and it will return an array that removed any duplicate objects that reference the same object in memory. There can be objects with the same properties, but they must be different in-memory objects. I know that objects are stored by reference in JS and this is what I have so far:
const unique = array => {
let set = new Set();
return array.map((v, index) => {
if(set.has(v.id)) {
return false
} else {
set.add(v.id);
return index;
}
}).filter(e=>e).map(e=>array[e]);
}
Any advice is appreciated, I am trying to make this with a very efficient Big-O. Cheers!
EDIT: So many awesome responses. Right now when I run the script with arbitrary object properties (similar to the answers) and I get an empty array. I am still trying to wrap my head around filtering everything out but on for objects that are referenced in memory. I am not positive how JS handles objects with the same exact key/values. Thanks again!
Simple Set will do the trick
let a = {'a':1}
let b = {'a': 1,'b': 2, }
let c = {'a':1}
let arr = [a,b,c,a,a,b,b,c];
function filterSameMemoryObject(input){
return new Set([...input])
}
console.log(...filterSameMemoryObject(arr))
I don't think you need so much of code as you're just comparing memory references you can use === --> equality and sameness .
let a = {'a':1}
console.log(a === a ) // return true for same reference
console.log( {} === {}) // return false for not same reference
I don't see a good reason to do this map-filter-map combination. You can use only filter right away:
const unique = array => {
const set = new Set();
return array.filter(v => {
if (set.has(v.id)) {
return false
} else {
set.add(v.id);
return true;
}
});
};
Also if your array contains the objects that you want to compare by reference, not by their .id, you don't even need to the filtering yourself. You could just write:
const unique = array => Array.from(new Set(array));
The idea of using a Set is nice, but a Map will work even better as then you can do it all in the constructor callback:
const unique = array => [...new Map(array.map(v => [v.id, v])).values()]
// Demo:
var data = [
{ id: 1, name: "obj1" },
{ id: 3, name: "obj3" },
{ id: 1, name: "obj1" }, // dupe
{ id: 2, name: "obj2" },
{ id: 3, name: "obj3" }, // another dupe
];
console.log(unique(data));
Addendum
You speak of items that reference the same object in memory. Such a thing does not happen when your array is initialised as a plain literal, but if you assign the same object to several array entries, then you get duplicate references, like so:
const obj = { id: 1, name: "" };
const data = [obj, obj];
This is not the same thing as:
const data = [{ id: 1, name: "" }, { id: 1, name: "" }];
In the second version you have two different references in your array.
I have assumed that you want to "catch" such duplicates as well. If you only consider duplicate what is presented in the first version (shared references), then this was asked before.

Categories