I am trying to find if there is a way to sort objects within an object using a bool value. I haven't been able to find anything to help, and not even sure if possible.
My object example is this:
{
"Music": {
"Key": "Music",
"Title": "Music",
"Icon": "t-music",
"Colour": "blue",
"Active": false
},
"The Arts": {
"Key": "The Arts",
"Title": "The Arts",
"Icon": "t-arts",
"Colour": "blue",
"Active": false
},
"Social": {
"Key": "Social",
"Title": "Social",
"Icon": "t-social",
"Colour": "yellow",
"Active": true
}
}
Is there anyway to sort these objects within the parent object based on the "Active" bool?
Though objects don't have a sort order, you can organize these objects in an array based on the Active bool. Use .sort() and .map().
var obj = {
"Music": {
"Key": "Music",
"Title": "Music",
"Icon": "t-music",
"Colour": "blue",
"Active": false
},
"The Arts": {
"Key": "The Arts",
"Title": "The Arts",
"Icon": "t-arts",
"Colour": "blue",
"Active": true
},
"Social": {
"Key": "Social",
"Title": "Social",
"Icon": "t-social",
"Colour": "yellow",
"Active": true
}
};
var sorted = Object.keys(obj) // ["Music", "The Arts", "Social"]
.sort(function(a, b) {
return obj[b].Active - obj[a].Active; // Organize the category array
})
.map(function(category) {
return obj[category]; // Convert array of categories to array of objects
});
The concept of order in an object doesn't exist, but you can shuffle the entries visually by turning the elements into an array, sorting it, and making a new object from that.
You can sort from a nested value by using Array#sort and passing it a function.
let arr = Object.entries(obj).sort(([key1, val1], [key2, val2]) => val2.Active)
arrayToObject = array => {
let newObj = {}
array.forEach(([key, val]) => {
newObj[key] = val
})
return newObj
}
console.log(arrayToObject(arr))
// { Social:
// { Key: 'Social',
// Title: 'Social',
// Icon: 't-social',
// Colour: 'yellow',
// Active: true },
// Music:
// { Key: 'Music',
// Title: 'Music',
// Icon: 't-music',
// Colour: 'blue',
// Active: false },
// 'The Arts':
// { Key: 'The Arts',
// Title: 'The Arts',
// Icon: 't-arts',
// Colour: 'blue',
// Active: false } }
Thanks Guys,
I wasn't sure if objects could be sorted but never hurts to ask.
Borja's answer worked for me, but only once i used Thomas' 'obj[b].Active - obj[a].Active' in place of '!obj[a].Active && obj[b].Active'.
I have only just seen Andrew's response and will test that out also.
Related
HHello. I have a mapping issue. I have nested data and i need to manupulate it to pick some values from nested array and make them higher level key-values. Here is the data i have and the data i want.
Data i have;
[
{
"id": 1,
"sku": "24-MB01",
"name": "Joust Duffle Bag",
"price": 34,
"custom_attributes": [
{
"attribute_code": "image",
"value": "/m/b/mb01-blue-0.jpg"
},
{
"attribute_code": "small_image",
"value": "/m/b/mb01-blue-0.jpg"
},
{
"attribute_code": "thumbnail",
"value": "/m/b/mb01-blue-0.jpg"
}
]
},
{
"id": 2,
"sku": "24-MB04",
"name": "Strive Shoulder Pack",
"price": 32,
"custom_attributes": [
{
"attribute_code": "small_image",
"value": "/m/b/mb04-black-0.jpg"
},
{
"attribute_code": "image",
"value": "/m/b/mb04-black-0.jpg"
},
{
"attribute_code": "description",
"value": "<p>Convenience is next to nothing when your day is crammed with action. So whether you're heading to class, gym, or the unbeaten path, make sure you've got your Strive Shoulder Pack stuffed with all your essentials, and extras as well.</p>\n<ul>\n<li>Zippered main compartment.</li>\n<li>Front zippered pocket.</li>\n<li>Side mesh pocket.</li>\n<li>Cell phone pocket on strap.</li>\n<li>Adjustable shoulder strap and top carry handle.</li>\n</ul>"
},
{
"attribute_code": "activity",
"value": "5438,5448,5450,5445"
}
]
}
]
The Data I want;
[
{
"id": 1,
"sku": "24-MB01",
"name": "Joust Duffle Bag",
"price": 34,
"image":"/m/b/mb01-blue-0.jpg"
},
{
"id": 2,
"sku": "24-MB04",
"name": "Strive Shoulder Pack",
"price": 32,
"image":"/m/b/mb04-black-0.jpg"
}
]
What i have so far;
var items = products.items.map(item => {
const custom_attr = item.custom_attributes.find(attr => !!attr.image) || {};
delete item.custom_attributes;
return {
...item,
...custom_attr
};
});
So basically i dont need the nested array, i just need the image(or maybe another attribute) data. But in the array all keys are the same(code and value as u see). I've tryed some mapping but couldn't get there. So i could use some help. Thanks in advance :)
In order to extract the image custom attribute, you have to find() the entry whose attribute_code is image:
const items = data.map(({ custom_attributes, ...item }) => {
const image = custom_attributes.find(
({ attribute_code }) => attribute_code === 'image'
)?.value;
return {
...item,
image,
};
});
Your code was pretty close. Instead of checking for !!attr, I assume what you meant to do was find the custom attribute with attribute: "image":
.find((attr) => attr.attribute_code === "image")
Additionally, instead of using delete (which will mutate the original object), you can use object destructuring and spread (...) to omit the custom_attributes property from the output object:
const products = {
items: [
{
id: 1,
sku: "24-MB01",
name: "Joust Duffle Bag",
price: 34,
custom_attributes: [
{
attribute_code: "image",
value: "/m/b/mb01-blue-0.jpg",
},
{
attribute_code: "small_image",
value: "/m/b/mb01-blue-0.jpg",
},
{
attribute_code: "thumbnail",
value: "/m/b/mb01-blue-0.jpg",
},
],
},
{
id: 2,
sku: "24-MB04",
name: "Strive Shoulder Pack",
price: 32,
custom_attributes: [
{
attribute_code: "small_image",
value: "/m/b/mb04-black-0.jpg",
},
{
attribute_code: "image",
value: "/m/b/mb04-black-0.jpg",
},
{
attribute_code: "description",
value:
"<p>Convenience is next to nothing when your day is crammed with action. So whether you're heading to class, gym, or the unbeaten path, make sure you've got your Strive Shoulder Pack stuffed with all your essentials, and extras as well.</p>\n<ul>\n<li>Zippered main compartment.</li>\n<li>Front zippered pocket.</li>\n<li>Side mesh pocket.</li>\n<li>Cell phone pocket on strap.</li>\n<li>Adjustable shoulder strap and top carry handle.</li>\n</ul>",
},
{
attribute_code: "activity",
value: "5438,5448,5450,5445",
},
],
},
],
};
const items = products.items.map(({ custom_attributes, ...item }) => {
const custom_attr =
custom_attributes.find((attr) => attr.attribute_code === "image") || {};
return {
...item,
...custom_attr,
};
});
console.log(items);
I have a JavaScript array with the following format:
[
{
"header": true,
"id": "0",
"name": "dairy",
},
{
"category": "dairy",
"header": false,
"id": "-LSlje6ESGALGpckMhb7",
"name": "milk",
},
{
"category": "dairy",
"header": false,
"id": "-LSm9EpFg5DhW036aUle",
"name": "cheese",
},
{
"header": true,
"id": "3",
"name": "dessert",
},
{
"category": "dessert",
"header": false,
"id": "-LSm9MLZkrnvtPySw5U6",
"name": "cake",
},
{
"category": "dessert",
"header": false,
"id": "-LSmAQ0rdDLrpz0TSPuD",
"name": "pie",
},
{
"header": true,
"id": "6",
"name": "fruit",
},
{
"category": "fruit",
"header": false,
"id": "-LSlazVIGAKLakxAIa8G",
"name": "apple",
},
{
"category": "fruit",
"header": false,
"id": "-LSlb5GH6xZz-DpNVS22",
"name": "pear",
},
{
"category": "fruit",
"header": false,
"id": "-LSwWJldY1nxQrotyv-V",
"name": "strawberry",
},
{
"header": true,
"id": "10",
"name": "meat",
},
{
"category": "meat",
"header": false,
"id": "-LSljXQzfXthJbOA54Ah",
"name": "fish",
},
{
"category": "meat",
"header": false,
"id": "-LSmA2-R9pOY8abAUyST",
"name": "steak",
},
{
"category": "meat",
"header": false,
"id": "-LSmAJ4J4gIfVQ8sgPDa",
"name": "pork",
},
]
What I am trying to do, is map through this array, and transform it to the following format:
[
{
title: nameOfFirstHeader,
data: items.slice(indexOfFirstHeader, indexOfSecondHeader),
},
{
title: nameOfSecondHeader,
data: items.slice(indexOfSecondHeader, indexOfThirdHeader),
},
{
title: nameOfThirdHeader,
data: items.slice(indexOfThirdHeader, indexOfFourthHeader),
},...and so on
]
So basically there will be an object section for each 'header' that is found in the original array. Each object section data property will contain the items found between the first header and the second header, and so on, until there are no more headers. I really can't wrap my head around how I can do this. Here is a reference to the the module I am using: https://github.com/saleel/react-native-super-grid#sectiongrid-example
Thanks!
I think this may be what you're trying to accomplish...
var grouped = items.reduce((acc,obj)=>{
let {header, name} = obj;
if (header) return [...acc, { title:name, data:[] }] // either first matching header or new match. Add fresh 'header' object
if (!acc.length) return acc; //not header and none have passed. Do nothing
let allButLast = acc.slice(0, acc.length-1),
lastElem = acc[acc.length-1]; // not a header, but there is an existing match. Add it to last match's data array
return [
...allButLast,
{
...lastElem,
data:[...lastElem.data, obj]
}
]
},[])
but it seems unreliable to trust the order of an array for this purpose. It would probably be more reliable to match by isHeader.name === notHeader.category to be less presumptive about the order of data you're iterating over. Like this...
var grouped = items.reduce((acc,obj)=>{
let {header, name, category} = obj;
if (header) return [...acc, { title:name, data:[] }];
if (!acc.length) return acc;
return acc.map((elem)=>{
if (elem.title !== category) return elem;
return {
...elem,
data: [ ...elem.data, obj]
};
})
},[])
I think you can probably do something like
const data = [];
let activeIndexForData = -1;
for(let i = 0; i < dataToSort.length -1; i++) {
if(dataToSort[i].header) {
activeIndexForData++;
}
if(data.length < activeIndexForData - 1) {
data.push({ title: dataToSort[i].name, data# []})
}
else {
data[activeIndexForData].data.push({ title: dataToSort[i].name, data: [])
}
}
{
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"column_0": {
"value": "1",
"label": "name"
},
"condition_0": {
"value": "101",
"label": "Is equal to"
},
"column_1": {
"value": "2",
"label": "age"
},
"condition_1": {
"value": "102",
"label": "Is less than"
}
}
I have the above object in JavaScript. From this object I need to create the following object. Need to find a way which is good from performance point of view. The below conditions array is based on the object starting with 'column_' in the above object.
For example: if there are column_0, column_1, column_2, the length of conditions array will be 3. These columns will be coming dynamically, can be from 0-n, n = any integer >= 0. (i.e. column_0 - column_n)
The same condition applies for condition_0, condition_1. Also, condition_0 is always associated with column_0, condition_1 is always associated with column_1 ans so on.
{
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"conditions" : [
{
"column_id": 1, // column_0.value
"column_name": "name", // column_0.label
"condition_id": 101 // condition_0.value
},
{
"column_id": 2, // column_1.value
"column_name": "age", // column_1.label
"condition_id": 102 // condition_1.value
}
],
}
extract the conditions using ...rest, reduce the Object.entries , construct the data structure and push it to the resulting array, finally put everything back together :
const data = {
"name": "test name",
"description": "test desc",
"data_table_id": 3,
"column_0": {
"value": "1",
"label": "name"
},
"condition_0": {
"value": "101",
"label": "Is equal to"
},
"column_1": {
"value": "2",
"label": "age"
},
"condition_1": {
"value": "102",
"label": "Is less than"
}
}
const {
name,
description,
data_table_id,
...rest
} = data;
const conditions = Object.entries(rest).reduce((all, [key, obj]) => {
if (key.startsWith('condition')) {
const id = key.split('_')[1];
const condition = {
"column_id": rest[`column_${id}`].value,
"column_name": rest[`column_${id}`].label,
"condition_id": obj.value,
}
all.push(condition)
}
return all;
}, []);
const result = {
name,
description,
data_table_id,
conditions
}
console.log(result)
I have a nested object that I wish to retrieve certain key/value pairs from. For these to be retrieved they must have a value for each key.
I'm close, but I'm not getting any of the nested objects' key/value pairs.
I have created this fiddle
Here's the function I have at present:
function getKeysVals(obj, keys, recurse = false)
{
let addToOutput = true;
let out = [];
let cnt = 0;
obj.map
(
(thisObj) =>
{
let newObj = {}; // Temp holder for new object that gets added to output.
// Loop through the requested keys, adding them to the new object:
for( i in keys)
{
// Check that this key has a value:
if(!thisObj[keys[i]])
{
addToOutput = false;
break;
}
else
{
newObj[keys[i]] = thisObj[keys[i]];
}
}
// Ensure we have values for ALL the requested keys in this object:
if( addToOutput ) out.push(newObj);
// Go round again if this object has the specified recurse object:
if( thisObj[recurse] )
{
getKeysVals(thisObj[recurse], keys, recurse)
}
}
);
return out
}
When I call it with result = getKeysVals(nodes[0].nodes, ['id', 'text', 'filePath'], 'nodes'); I expect to get a new array with:
[
{ id: 1526297185466, text: 'test part a', filePath: 'test part a-1526297185451.CSV' },
{ id: 1526297202132, text: 'test part B', filePath: 'test part B-1526297202118.CSV' },
{ id: 1526297209980, text: 'Test Part C', filePath: 'Test Part C-1526297209966.CSV' }
]
But I only get:
[{ id: 1526297185466, text: 'test part a', filePath: 'test part a-1526297185451.CSV' }]
The whole object:
[{
"id": 1526297177970,
"text": "k",
"nodes": [
{
"id": 1526297185466,
"tags": [1],
"text": "test part a",
"state": { "checked": true, "expanded": true },
"filePath": "test part a-1526297185451.CSV"
},
{
"id": 1526297195199,
"tags": [1],
"text": "New Product Set",
"nodes": [
{
"id": 1526297202132,
"tags": [1],
"text": "test part B",
"state": { "checked": true, "expanded": true },
"filePath": "test part B-1526297202118.CSV"
},
{
"id": 1526297209980,
"tags": [1],
"text": "Test Part C",
"state": { "checked": true, "expanded": true },
"filePath": "Test Part C-1526297209966.CSV"
}
],
"state": { "checked": true }
}
],
"state": { "checked": true }
}]
If you call
getKeysVals(thisObj[recurse], keys, recurse)
This will create a new out and return that, so you may add that to the current out:
out.push(...getKeysVals(thisObj[recurse], keys, recurse));
I get an array of objects representing books. Book has nested Chapters and each chapter has nested Pages. I am trying to reach pages using where and/or chain and name of the chapter but don't know how to reference pages to iterate and get name and keyword. None of my approaches worked and obviously I am missing a key understanding.
var getPages = function(book, n) {
_.chain(book.chapters).where({name:n})...how do I refer pages array from here?;
or
_.select(_.where(book.chapters,{name:n}), function(p) {
return p.keyword + p.name;
};
};
Nested Data:
{
"name": "Javascript for Dummies",
"chapters": [
{
"name": "Introduction",
"status": "passed",
"pages": [
{
"name": "page 10",
"keyword": "objects",
"status": "passed"
},
{
"name": "page 40",
"keyword": "methods",
"status": "failed"
},
{
"name": "",
"keyword": "",
"status": ""
}
]
},
{
"name": "Data Types",
"status": "passed",
"pages": [
{
"name": "page 33",
"keyword": "Strings",
"status": "passed"
},
{
"name": "",
"keyword": "",
"status": ""
},
{
"name": "",
"keyword": "",
"status": ""
}
]
}
],
"status": "failed"
}
You are almost there, just get only the pages property from all the returned values and flatten them like this
function getPages(book, n) {
return _.chain(book.chapters).where({
name: n
})
.map(function(currentObject) {
return currentObject.pages;
})
.flatten()
.value();
}
Output
[ { name: 'page 10', keyword: 'objects', status: 'passed' },
{ name: 'page 40', keyword: 'methods', status: 'failed' },
{ name: '', keyword: '', status: '' } ]
Here, _.where returns an array and we are just iterating through the array and creating a new array with only the pages object from individual objects. Since there may be more than one matches, the result will be an array of array of pages, so we are flattening it and finally getting the value and returning it.