Say I have the array below, and I want to retrieve the keys inside the indexed object, so that I would end up with an array like this ["prop1", "prop2", "prop3", "prop3"]
const testArray = [
{
0: {
prop1: "sdsd",
prop2: "ssdsd"
}
},
{
1: {
prop3: "sdsd",
prop4: "sddd"
}
}
]
Using reduce and concat:
const testArray = [
{
0: {
prop1: "sdsd",
prop2: "ssdsd"
}
},
{
1: {
prop3: "sdsd",
prop4: "sddd"
}
}
]
const keys = testArray.reduce((arr, curr, index) =>
arr.concat(Object.keys(curr[index])),[])
console.log(keys)
// ["prop1", "prop2", "prop3", "prop4"]
You can map over the array, taking the value's keys
const testArray = [
{
0: {
prop1: "sdsd",
prop2: "ssdsd"
}
},
{
1: {
prop3: "sdsd",
prop4: "sddd"
}
}
]
const keys = testArray
.map(e => Object.keys(...Object.values(e)))
.flat();
console.log(keys);
Note that flat() is experimental for now, so you might want to use reduce or so to flatten the array...
#bambam has given the solution with "map", which is clearer.
You can also do that with "reduce" as below.
testArray.reduce((arr, d) =>
(arr.push(
Object.entries(d).map(([, l]) => Object.keys(l))
)
, arr)
,[]).flat(2)
One solution with forEach
const testArray = [
{
0: {
prop1: "sdsd",
prop2: "ssdsd"
}
},
{
1: {
prop3: "sdsd",
prop4: "sddd"
}
}
];
var arr = [];
testArray.forEach((data, index) => {
let tempArr = Object.keys(data[index]);
arr = arr.concat(tempArr);
})
console.log(arr);
const testArray = [
{
0: {
prop1: "sdsd",
prop2: "ssdsd"
}
},
{
1: {
prop3: "sdsd",
prop4: "sddd"
}
}
];
var arr = testArray.map((p)=>{return Object.keys(p[Object.keys(p)[0]]);});
console.log([].concat.apply([],arr));
Related
I have two objects, that contains same number of properties:
object1: {
prop1: 'data12',
prop2: 'data13',
prop3: 'data58',
prop4: 'xyz',
// more props here...
}
object2: {
prop1: 123,
prop2: 'email#gmail.com',
prop3: 'text',
prop4: 'bla',
// more props here...
}
I want to build an array {name: '', value: ''} based on this objects:
[
{ data12: 123 },
{ data13: 'email#gmail.com' },
{ data58: text },
{ xyz: 'bla' },
]
How can you do that? The number of properties could be different.
Just using Object.keys() can do it
let object1 = {
prop1: 'data12',
prop2: 'data13',
prop3: 'data58',
prop4: 'xyz',
}
let object2 = {
prop1: '123',
prop2: 'email#gmail.com',
prop3: 'text',
prop4: 'bla',
}
let result =[]
let obj = {}
Object.keys(object1).forEach(k =>{
obj[object1[k]] = object2[k]
})
result.push(obj)
// combined with reduce can also do it
/*
let obj =Object.keys(object1).reduce((a,c) => {
a[object1[c]] = object2[c]
return a
},{})
*/
result.push(obj)
console.log(result)
I am programming a function that will handle javascript array filtering. I know the values by which I want to filter so I know how to do it in a fairly easy way, but I would like the code to be more extensible.
I wrote such a function:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
if (filters.prop3 && filters.prop2) {
return item.prop3 === filters.prop3 && item.prop2 === filters.prop2;
}
if (filters.prop3) {
return item.prop3 === filters.prop3;
}
if (filters.prop2) {
return item.prop2 === filters.prop2;
}
});
}
I am not completely satisfied with it. I think it could be written better. If the 3rd argument comes, I don't want to add it to the if - it should be automatic.
I've searched several topics on stackoverflow, looked through the lodash documentation looking for some good solution but I have no idea what I can do better with this.
If I understood correctly, you want to only keep the items that match all the filter:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => {
return items.filter((item) =>
Object.entries(filters).every(([key, val]) => item[key] === val)
)
};
console.log(handleFilters(items, filters))
This basically checks that every (key, val) of the filter exists in the item
private handleFilters (items, ...props) {
return items.filter(item => props.every(prop => item[prop] === prop));
}
To use the filter object dynamically, you could look into using something like Object.keys().
Here's your example code changed to use that:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter(item => {
return Object.keys(filters).some(filterKey => item[filterKey] === filters[filterKey])
});
}
It loops through the items, the same as you already were doing. However, now it also loops through the keys set in the filters object. The some() function returns a boolean; true if the callback is true and false if the callback is false. Here it checks if the value in item[filterKey] is the same as the value in filters[filterKey].
It's important to note that this returns the item if any of the filter values matches with an item's property. If all values must be in the filtered item, you'll want to use Object.keys(filters).every(filterKey => item[filterKey] === filters[filterKey]).
The every function returns true only if all filterKeys have the same value as the keys checked in item.
It is also possible to use Object.entries(), which allows directly using the value, but for this example I chose to use just the keys, for consistency.
You can filter by using Object.key , filter and every method in precise manner.
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilter = (items, filters) => {
return items.filter((item) => Object.keys(filters).every((key) => item[key] === filters[key]));
}
console.log(handleFilter(items, filters))
You can remove your multiple if blocks by using for..in
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
for(const key in filters) {
if (filters[key] !== item[key]) {
return false;
}
}
return true;
});
}
console.log(handleFilters(items, filters))
It does the same as your code was doing.
Some Improvement over #David Alvarez approach, here i have used Object.keys instead of Object.entries as Object.key is faster than Object.entries here is the comparison: https://www.measurethat.net/Benchmarks/Show/3685/0/objectentries-vs-objectkeys-vs-objectkeys-with-extra-ar
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => (
items.filter((item) =>
Object.keys(filters).every(key => item[key] === filters[key])
)
);
console.log(handleFilters(items, filters))
I have an array of objects that also contains array like this:
let array = [{"a": ["b", "c"]} , {"b":[ "d" ]}, {"e":[ "f" ]}]
I need to display it as Tree View and I need to get the output like below:
[
{
id : "a",
children: [
{
id: "b",
children: [{id: "d", children: [] }]
},
{
id: "c",
children: []
}
]
},
{
id: "e",
Children: [
{
id: "f",
children: []
}
]
}
]
I tried to do it by creating an array of all the parents [a, b, e], and depth first search through the entire array but I could not get the correct output.
Can someone help? Thanks in advance.
Try this recursive approach:
var array = [{ "a": ["b", "c"] }, { "b": ["d"] }, { "e": ["f"] }]
let object = {}
array.map(itm => Object.keys(itm).map(name => object[name] = itm[name]))
const treeView = (key, val) => {
delete object[key]
return {
id: key,
children: getChildrens(val)
}
}
const getChildrens = (arr) => {
let childrens = []
return arr.map(itm => {
if (object[itm]) {
return treeView(itm, object[itm])
}
return { id: itm, children: [] }
})
}
let res = Object.keys(object).map((val, idx) => {
if (object[val])
return treeView(val, object[val])
}).filter(e=>e)
console.log(res)
Consider I have a nested object array. One possible example scenario could be:
content: [
{
prop1: someValue,
prop2: someValue,
content: [
{
prop2: someValue,
prop3: someValue,
myProperty: myValue
},
{
prop1: someValue,
prop3: someValue,
myProperty: otherValue
}
]
},
{
prop5: someValue,
prop2: someValue
}
]
Here are the possibilities:
The structure starts with content[] but the descendants may or may not have content property.
The level of the hierarchy can be of any number.
The properties contained by the objects are not always the same i.e. one object may have x, y, z properties while the other may have v, w, z properties.
If any object in the hierarchy has myProperty key, there won't be content key.
More than one object in the hierarchy can have myProperty with value'myValue.
My requirement:
If at any level an object has the property myProperty with the value myValue then remove the entire object (NOT JUST THE PROPERTY) from the hierarchy.
My attempt so far:
private removeObjects(content: any, values: string[]): any {
if (!content || content.length === 0) {
return
}
content = content.filter((c) => {
if (!c.myProperty) return true
return c.myProperty.indexOf(values) > 0
})
// Here is my problem since I am supposed to do a recursive call on each of child contents,
// how do I merge back the original array?
return this.removeObjects(content, values)
}
The following recursively returns a new array without mutating the original
const content = [{
prop1: "someValue",
prop2: "someValue",
content: [{
prop2: "someValue",
prop3: "someValue",
myProperty: "myValue"
},
{
prop1: "someValue",
prop3: "someValue",
myProperty: "otherValue"
}
]
},
{
prop5: "someValue",
prop2: "someValue"
}
]
function removeObjects(content) {
return content.reduce((arr, obj) => {
if (obj["myProperty"] && obj["myProperty"] === "myValue") {
return arr
} else if (obj["content"] && obj["content"].length) {
arr.push({ ...obj,
content: removeObjects(obj["content"])
})
return arr
} else {
arr.push(obj);
return arr;
}
}, []);
}
console.log(removeObjects(content))
Expected output:
const content = [{
prop1: "someValue",
prop2: "someValue",
content: [
{
prop1: "someValue",
prop3: "someValue",
myProperty: "otherValue"
}
]
},
{
prop5: "someValue",
prop2: "someValue"
}
]
You can use below function to get expected result:
let data = {
content: [
{
prop1: 'someValue',
prop2: 'someValue',
content: [
{
prop2: 'someValue',
prop3: 'someValue',
myProperty: 'myValue'
},
{
prop1: 'someValue',
prop3: 'someValue',
myProperty: 'otherValue'
}
]
},
{
prop5: 'someValue',
prop2: 'someValue'
}
]
}
function removeMyvalyeObj(data) {
for (let i = data.content.length - 1; i >= 0; i--) {
if (data.content[i].myProperty === 'myValue') {
data.content.splice(i, 1);
} else if(data.content[i].content) {
removeMyvalyeObj(data.content[i])
}
}
}
removeMyvalyeObj(data);
console.log(data);
Try this, (this is JavaScript version)
let someValue = 'a';
let otherValue ='x';
let myValue = 'xy';
let content = [
{
prop1: someValue,
prop2: someValue,
content: [
{
prop2: someValue,
prop3: someValue,
myProperty: myValue
},
{
prop1: someValue,
prop3: someValue,
myProperty: otherValue
}
]
},
{
prop5: someValue,
prop2: someValue
}
];
function removeObjects(content, values) {
debugger;
if (!content || content.length === 0) {
return
}
content = content.filter((c) => {
if (!c.myProperty) return true
return c.myProperty.indexOf(values) > 0
})
// Here is my problem since I am supposed to do a recursive call on each of child contents,
// how do I merge back the original array?
return content.map(x => ({
...x,
content: removeObjects(x.content, values)
}))
}
console.log(removeObjects(content, 'x'))
Use recursive approach, to have filter array of items and their content array.
const filter = (arr, name, value) =>
arr
.filter((obj) => !(name in obj && obj[name] === value))
.map((obj) => {
const nObj = { ...obj };
if (obj.content && Array.isArray(obj.content)) {
nObj.content = filter(obj.content, name, value);
}
return nObj;
});
content = [
{
prop1: 'someValue',
prop2: 'someValue',
content: [
{
prop2: 'someValue',
prop3: 'someValue',
myProperty: 'myValue',
},
{
prop1: 'someValue',
prop3: 'someValue',
myProperty: 'otherValue',
},
],
},
{
prop5: 'someValue',
prop2: 'someValue',
},
];
const updated = filter(content, "myProperty", "myValue");
console.log(updated);
javascript return array of nested object and arrange similar types
I have objects
let obj1 = { categoryId:1, category:"Fruits", name:"Orange"}
let obj2 = { categoryId:1, category:"Fruits",name:"Apple"}
let obj3 = { categoryId:2, category:"Vegetables", name:"Onion"}
let obj4 = { categoryId:2, category:"Vegetables", name:"Ginger"}....etc
I want to create a array of nested object from this array expected:
let result =
[
{
name: "Fruits", values: [
{
"categoryId": 1,
"category": "Fruits",
"name": "Orange"
},
{
"categoryId": 1,
"category": "Fruits",
"name": "Apple"
}
]
},
{ name:"Vegetables", values: [
{
"categoryId": 2,
"category": "Vegetables",
"name": "Onion"
},
{
"categoryId": 2,
"category": "Vegetables",
"name": "Ginger"
}
]
}
]
I am looking for a function to add to the map one by one not all at once
addtoArray( obj1);
addtoArray( obj2);
addtoArray( obj3);
addtoArray( obj4);....etc
Also a remove function:
removeFromArray( obj1);
In delete the object must not be same:
{ categoryId:1, category:"Fruits", name:"Orange", newItem:"newvalue"}
It should be deleted using category:"Fruits" reference
Deleting an object, until no more objects are in the group. Then the group gets removed as well.
What I tried is below
function addtoArray(collection, object) {
if (!collection[object.category]) {
collection[object.category] = [object];
return;
}
if (collection[object.category].includes(object)) return;
collection[object.category].push(object);
}
function removeFromArray(collection, object) {
if (!collection[object.category]) return;
var index = collection[object.category].indexOf(object);
if (index === -1) return;
collection[object.category].splice(index, 1);
if (!collection[object.category].length) delete
collection[object.category];
}
var obj1 = { categoryId: 1, category: "Fruits", name:"Orange" },
obj2 = { categoryId: 1, category: "Fruits", name:"Apple" },
obj3 = { categoryId: 2, category: "Vegetables", name:"Onion" },
obj4 = { categoryId: 2, category: "Vegetables", name:"Ginger" },
collection = {};
Please guide me!!
Function for adding to the array:
var arr=[];
const addToArray=(arr,obj)=>{
const index=findByName(arr,obj.category);
if(index>-1){
arr[i].values.push(obj)
}else{
arr.push({
name:obj.category,
values:[obj]
})
}
}
const findByName=(arr,name)=>{
for(var i=0;i<arr.length;i++){
if(arr[i].name===name)
return i;
}
return -1;
}
addToArray(arr,obj1)
Since you're doing various lookups on your data, array is not the recommended way to go.
You'll need to traverse it each time you'd like to take out an object.
Instead, use a class with some helpers.
The first step is adding unique ids to your data:
let obj1 = { id: 0, categoryId:1, category:"Fruits", name:"Orange"}
let obj2 = { id: 1, categoryId:1, category:"Fruits",name:"Apple"}
let obj3 = { id: 2, categoryId:2, category:"Vegetables", name:"Onion"}
let obj4 = { id: 3, categoryId:2, category:"Vegetables", name:"Ginger"}
The second part is create a class to hold this data and allow you to manipulate it:
class MyCollection {
constructor() {
this.items = {};
}
addItem(item) {
this.items[item.id] = item;
}
removeItem(item) {
delete this.items[item.id]
}
removeGroup(group) {
for(key in this.items) {
if(this.collection[key] === group) {
delete this.collection[key];
}
}
}
toArray() {
return Object.values(this.items);
}
}
usage:
const collection = new MyCollection();
collection.add(obj1);
collection.add(obj2);
collection.add(obj3);
collection.removeGroup("Fruits");
collection.items; // {2: { id: 2, categoryId:2, category:"Vegetables", name:"Onion"} }
//or should you desire to return an array
collection.toArray(); // [{ id: 2, categoryId:2, category:"Vegetables", name:"Onion"}]
Does the structure HAVE to be like the one you showed above? In my opinion, a following structure would be much cleaner:
const result = {
Fruits: [ {categoryId: 1, category: "Fruits", name: "orange"}, ... ],
Vegetables: [ ... ]
}
With this structure, your code would look like this:
let obj1 = { categoryId:1, category:"Fruits", name:"Orange"}
let obj2 = { categoryId:1, category:"Fruits",name:"Apple"}
let obj3 = { categoryId:2, category:"Vegetables", name:"Onion"}
let obj4 = { categoryId:2, category:"Vegetables", name:"Ginger"}
const objs = [ obj1, obj2, obj3, obj4 ]
const addToCollection = (collection, obj) => {
if (!collection[obj.category]) {
collection[obj.category] = []
}
collection[obj.category].push(obj)
}
const removeFromCollection = (collection, obj) => {
if (!collection[obj.category]) return
collection[obj.category] = collection[obj.category].filter((o) => {
return o.name !== obj.name
})
}
const result = {}
objs.forEach((obj) => {
addToCollection(result, obj)
})
console.log(result)
removeFromCollection(result, obj2)
console.log(result)
Which is also quite efficient, because the correct category can be found quickly.
Output:
{
Fruits: [
{ categoryId: 1, category: 'Fruits', name: 'Orange' },
{ categoryId: 1, category: 'Fruits', name: 'Apple' }
],
Vegetables: [
{ categoryId: 2, category: 'Vegetables', name: 'Onion' },
{ categoryId: 2, category: 'Vegetables', name: 'Ginger' }
]
}
{
Fruits: [
{ categoryId: 1, category: 'Fruits', name: 'Orange' }
],
Vegetables: [
{ categoryId: 2, category: 'Vegetables', name: 'Onion' },
{ categoryId: 2, category: 'Vegetables', name: 'Ginger' }
]
}