Hey guys I have the following array that's used to display a flatlist within my app.
Array [
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
},
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "1,16,19",
},
]
However I would like to sort this array based off the subjects value. In the app the user can select a couple of subjects which are represented by numbers so lets say the users selected subjects are:
11, 4, 2, 1
I would like to sort the array so that the items with 3 or more subjects in common with the user are sorted to the top and then items with two and then 1 and then none so the array above should look like this after sorting:
Array [
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
},
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "0,16,19",
},
]
How can I achieve this?
I have been searching around the array sort function:
Array.prototype.sort()
However I have only seen how to sort based off number comparisons I have never seen an array sorted based off values in common. Please could someone help me with this!
EDIT
Array [
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
"ranking": "green",
},
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
"ranking": "amber",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
"ranking": "amber",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "0,16,19",
"ranking": "red",
},
]
You could create an object with counts of selected subjects and sort descending by this value.
const
data = [{ data: "Item 1", id: "1", subjects: "1,8,9,23,11,15,16,14,20" }, { data: "Item 2", id: "2", subjects: "8,11,2,4,16,19" }, { data: "Item 3", id: "3", subjects: "16,20,14,11,9,2" }, { data: "Item 4", id: "4", subjects: "1,16,19" }],
selected = [11, 4, 2, 1],
counts = data.reduce((r, { id, subjects }) => {
r[id] = subjects
.split(',')
.reduce((s, v) => s + selected.includes(+v), 0);
return r;
}, {});
data.sort((a, b) => counts[b.id] - counts[a.id]);
console.log(data);
console.log(counts);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
how can I assign object property value as property key?
I have a set of data:
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
]
I want to reformat it to become:
const mydata = [
{
"Title 1": "Value 1"
},
{
"Title 2": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
]
},
]
I have tried this code to achieve it:
mydata.map((dt: any) => {
dt.title: dt.value
});
However, it seems not working.
Any idea how can I reformat it to the one I desire?
Thanks.
Please use following code.
Reference URL How to use a variable for a key in a JavaScript object literal?
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
];
let reData = [];
mydata.forEach((dt)=>{
reData.push({[dt.title]: dt.value});
});
console.log(reData);
If you want to transform the array to a different type of variable, use [reduce][1]
const mydata = [
{
id: 001,
value: "Value 1",
title: "Title 1",
},
{
id: 002,
value: [
{
Name: "Name 1",
Age: "20",
},
{
Name: "Name 2",
Age: "30",
},
],
title: "Title 2",
},
];
const data = mydata.reduce(
(acc, cur) => ({ ...acc, [cur.title]: cur.value }),
{}
);
console.log(data);
Your map function has an error, and your key assignment has another one. Let's fix it.
const newData = mydata.map((dt: any) => ({
[dt.title]: dt.value,
}));
First: You can't return an object from an arrow function without parenthesis, if you don't use it, the code will think it is a function body not an object.
Second: If you want to return a value as a key, you need put it inside "[ ]" (Square brackets)
Just that, simple mistakes, at the end you came up with the right logic to solve it
Add brackets around the return value.
Use square brackets for a computed property name.
const mydata = [
{
"id": 001,
"value": "Value 1",
"title": "Title 1"
},
{
"id": 002,
"value": [
{
"Name": "Name 1",
"Age": "20"
},
{
"Name": "Name 2",
"Age": "30"
},
],
"title": "Title 2"
},
];
const res = mydata.map(({value, title})=>({[title]: value}));
console.log(res);
I have the following data that is an array of nested objects:
"categories": [
{
"id": 1,
"name": "Category 1",
"years": [
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" }
]
},
{
"id": 2,
"name": "Category 2",
"years": [
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" }
]
}
]
I want to extract unique years in a separate array (desired output):
[
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" },
]
When I map out the years, I'm getting an array of arrays, how should I extract the unique objects for years?
let years = categories.map( (c) => { return c.years })
You can use a Map to filter duplicate years from the array of values using id as the key, and reduce() on both categories and years using the map as the accumulator:
const categories = [{
"id": 1,
"name": "Category 1",
"years": [
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" }
]
},
{
"id": 2,
"name": "Category 2",
"years": [
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" }
]
}
];
const years = categories.reduce(
(map, category) => category.years.reduce(
(map, year) => map.set(year.id, year),
map
),
new Map()
);
console.log([...years.values()]);
You can use reduce and Map
let data = [{"id": 1,"name": "Category 1","years": [{ "id": 1, "name": "1" },{ "id": 2, "name": "2" }]},{"id": 2,"name": "Category 2","years": [{ "id": 2, "name": "2" },{ "id": 3, "name": "3" }]}]
let final = data.reduce((op,{years}) => {
years.forEach(({id, name}) => {
let key = id + '-' + name
op.set(key, op.get(key) || {id, name})
})
return op
},new Map())
console.log([...final.values()])
This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 4 years ago.
I'm new to Javascript, ES6 , and i have hit the wall with this problem
This is the JSON that i'm getting from a webservice
{
"products": [
{
"id": 2,
"id_default_image": "21",
"price": "35.900000",
"name": [
{
"id": "1",
"value": "item 1"
},
{
"id": "2",
"value": "item 1 alternate name"
}
]
},
{
"id": 4,
"id_default_image": "4",
"price": "29.000000",
"name": [
{
"id": "1",
"value": "item 2"
},
{
"id": "2",
"value": "item 2 alternate name"
}
]
}
]
}
The name property in the above JSON is an array and i need only the value of the first element. The desired output would be like below
{
"products": [
{
"id": 2,
"id_default_image": "21",
"price": "35.900000",
"name": "item 1"
},
{
"id": 4,
"id_default_image": "4",
"price": "29.000000",
"name": "item 2"
}
]
}
I'm working on a react-native project. What would be the easiest way to achieve this? Any help would be greatly appreciated. Thanks.
Similar to Ele's answer but if you don't want to change the original object. You can use map to iterate over the product objects and return a new products array:
const data = {"products":[{"id":2,"id_default_image":"21","price":"35.900000","name":[{"id":"1","value":"item 1"},{"id":"2","value":"item 1 alternate name"}]},{"id":4,"id_default_image":"4","price":"29.000000","name":[{"id":"1","value":"item 2"},{"id":"2","value":"item 2 alternate name"}]}]};
const products = data.products.map(obj => ({ ...obj, name: obj.name[0].value }));
console.log(products);
Also used: spread syntax
Well, you can use the function forEach or a simple for-loop and assign the first value to the attribute name.
let obj = { "products": [ { "id": 2, "id_default_image": "21", "price": "35.900000", "name": [ { "id": "1", "value": "item 1" }, { "id": "2", "value": "item 1 alternate name" } ] }, { "id": 4, "id_default_image": "4", "price": "29.000000", "name": [ { "id": "1", "value": "item 2" }, { "id": "2", "value": "item 2 alternate name" } ] } ]}
obj.products.forEach(o => (o.name = o.name[0].value));
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this
const productsArray = products.map((product, index) => {
const obj = {};
obj["id"] = product["id"];
obj["id_default_image"] = product["id_default_image"];
obj["price"] = product["price"],
obj["name"] = product.name[0].value;
return obj;
});
const obj = {};
obj.products = productsArray;
console.log(obj);//will print you the desired output you want
As an example - I've included a one element array that contains an object that has a Children key, which is an array of objects and each object also has its' own Children key that contains another array.
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
{
"Id": "100",
"DisplayName": "1-2",
},
]
}
]
}
]
There is a second array of objects that I would like to compare the first array of objects to, with the intention of making sure that the first array is in the same order as the second array of objects, and if it is not - then sort until it is.
Here is the second array:
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "100",
"DisplayName": "1-2",
},
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
]
}
]
}
]
The data that this will run on can be up in the tens of thousands - so performance is paramount.
What I'm currently attempting is using a utility method to convert each element of the second array into a keyed object of objects e.g.
{
1: {
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}
]
}
}
This allows fast look up from the top level. I'm wondering if I should continue doing this all the way down or if there is an idiomatic way to accomplish this. I considered recursion as well.
The order of the already sorted array is not based on Id - it is arbitrary. So the order needs to be preserved regardless.
Assuming same depth and all Id's exist in each level of each object use a recursive function that matches using Array#findIndex() in sort callback
function sortChildren(main, other) {
other.forEach((o, i) => {
if (o.children) {
const mChilds = main[i].children, oChilds = o.children;
oChilds.sort((a, b) => {
return mChilds.findIndex(main => main.Id === a.Id) - mChilds.findIndex(main => main.Id === b.Id)
});
// call function again on this level passing appropriate children arrays in
sortChildren(mChilds, oChilds)
}
})
}
sortChildren(data, newData);
console.log(JSON.stringify(newData, null, ' '))
<script>
var data = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "3",
"DisplayName": "1-2",
},
{
"Id": "4",
"DisplayName": "3-4",
},
]
}]
}]
var newData = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}]
}]
</script>
Loop may be the wrong term, but it kind of describes what I am attempting.
I want to give structure to flat data, but I also need to keep track of the array it came from.
Basically my rules are (per array):
If level 1 exists- give it the name of the item, and a typechild array. EACH time a level 1 appears (even in the same array) it should create a new entry.
Inside typechild, put the any items with level >1
If NO level 1 exists- give it the name of the item, and a typechild array.
My code below is almost there, with the exception that it should create an array EVERYTIME it sees a level 1. My example will make sense:
Input data
[
{
"title": "Test 1",
"type": [{
"name": "Animal",
"level": 1
},
{
"name": "Food",
"level": 1
},
{
"name": "Chicken",
"level": 3
}
]
},
{
"title": "Test 2",
"type": [{
"name": "Foo",
"level": 2
}]
}
]
Note: Animal and Food are both LEVEL 1 items. So it should create two ARRAYS like so...
Desired output
[
{
name: "Animal",
typechild: [
{
level: 2,
name: "Chicken"
}
]
},
{
name: "Food",
typechild: [
{
level: 2,
name: "Chicken"
}
]
},
{
name: "NoName",
typechild: [
{
level: 2,
name: "Foo"
}
]
}
]
Ramda attempt (try here: https://dpaste.de/JQHw):
const levelEq = (n) => pipe(prop('level'), equals(n));
const topLevel = pipe(prop('type'), find(levelEq(1)));
const topLevelName = pipe(topLevel, propOr('NoName', 'name'));
const extract2ndLevel = pipe(pluck('type'), flatten, filter(levelEq(2)));
const convert = pipe(
groupBy(topLevelName),
map(extract2ndLevel),
map(uniq),
toPairs,
map(zipObj(['name', 'typechild']))
);
Something like this?
var output = [{
"name": "Animal",
"typechild": [{
"name": "Chicken",
"level": 3
}, {
"name": "Dog",
"level": 2
}]
}, {
"name": "Food",
"typechild": [{
"name": "Chicken",
"level": 3
}]
}, {
"name": "No name",
"typechild": [{
"name": "Foo",
"level": 2
}, {
"name": "Baz",
"level": 2
}]
}]
let out = {},
typechild = {},
k;
const data = [{
"title": "Test 1",
"type": [{
"name": "Animal",
"level": 1
}, {
"name": "Food",
"level": 1
}, {
"name": "Chicken",
"level": 3
}]
}, {
"title": "Test 2",
"type": [{
"name": "Foo",
"level": 2
}]
}, {
"title": "Test 3",
"type": [{
"name": "Baz",
"level": 2
}]
}, {
"title": "Test 4",
"type": [{
"name": "Animal",
"level": 1
}, {
"name": "Dog",
"level": 2
}]
}]
data.forEach((node) => {
k = false;
typechild[node.title] = [];
node.type && node.type.forEach((t, i) => {
if (t.level == 1) {
k = true;
!out[t.name] ? out[t.name] = {
name: t.name,
typechild: typechild[node.title]
} : out[t.name].typechild = out[t.name].typechild.concat(typechild[node.title]);
} else {
typechild[node.title].push(t);
}
if (i == node.type.length - 1 && !k && typechild[node.title].length) {
out['No name'] = out['No name'] || {
name: 'No name',
typechild: []
};
out['No name'].typechild = out['No name'].typechild.concat(typechild[node.title]);
}
});
});
console.log(JSON.stringify(Object.values(out)));