Javascript Merge/filter array - javascript

Given the following json object:
var posts = {
[0] : {
"name": "X",
"categories" : [1, 5, 6]
},
[1] : {
"name": "Y",
"categories" : [1, 5, 7]
}
}
How can I get a single array containing every "categories" property value, without repetitions?
In this case, I would like to retrieve something like
var objCategories = [1, 5, 6, 7];

In a ES6 enabled environment, you can use a Set and reduce
let objCategories = [...new Set(Object.values(posts).reduce((a,b) => a.concat(b.categories), []))];
let posts = {
[0]: {
"name": "X",
"categories": [1, 5, 6]
},
[1]: {
"name": "Y",
"categories": [1, 5, 7]
}
};
let cats = [...new Set(Object.values(posts).reduce((a, b) => a.concat(b.categories), []))];
console.log(cats);

You have to loop through the object:
var posts = {
[0] : {
"name": "X",
"categories" : [1, 5, 6]
},
[1] : {
"name": "Y",
"categories" : [1, 5, 7]
}
}
var objCategories = [];
Object.values(posts).forEach(post => {
post.categories.forEach(c => {
if (objCategories.indexOf(c) === -1) {
objCategories.push(c);
}
})
});
console.log(objCategories);

something like
var objCategories = {}; //create a map for all unique keys
Object.keys(posts).forEach( function(index){
var post = posts[ index ]; //get access to the object
post.categories.forEach( function(cat){
objCategories[ cat ] = cat;
});
});
objCategories = Object.keys( objCategories ); //get only the arrays of categories and assign them back to objCategories for desired result

Use reduce and forEach:
var posts = {
[0] : {
"name": "X",
"categories" : [1, 5, 6]
},
[1] : {
"name": "Y",
"categories" : [1, 5, 7]
}
};
var result = Object.keys(posts).reduce(function (categories, key) {
posts[key].categories.forEach(function (category) {
if (categories.indexOf(category) === -1) {
categories.push(category);
}
});
return categories;
}, []);
console.log(result);

do
var posts = {
[0] : {
"name": "X",
"categories" : [1, 5, 6]
},
[1] : {
"name": "Y",
"categories" : [1, 5, 7]
}
}
// logic
var merged = []; // get all of the categories
for (let name in posts) { // loop through name
var post = posts[name]; // get post
merged = merged.concat(post.categories); // add categories
}
var unique = merged.filter(function(item, pos, self) { // filter duplicates
return self.indexOf(item) == pos;
});
console.log(unique);
exp:
this gathers all the categories
for (let name in posts) { // loop through name
var post = posts[name]; // get post
merged = merged.concat(post.categories); // add categories
}
the result will be
[1, 5, 6, 1, 5, 7]
then this filters out the duplicates:
var unique = merged.filter(function(item, pos, self) { // filter duplicates
return self.indexOf(item) == pos;
});
the result is"
[1, 5, 6, 7]

Related

Convert key, values JSON array to tabular JSON format

I have following JSON data for Chart
var chartJson = [
{
header : '2016',
values : [1, 5, 9]
},
{
header : '2017',
values : [2, 4, 8]
},
{
header : '2018',
values : [3, 1, 5]
}
];
And needs to convert it into this format to feed my HTML table
var tableJson = [
{
2016 : 1,
2017 : 2,
2018 : 3
},
{
2016 : 5,
2017 : 4,
2018 : 1
},
{
2016 : 9,
2017 : 8,
2018 : 5
}
];
Any quick help will be appreciated to convert it into this format.
I tried using this code, but somehow missing on the logic.
let table = [];
for(var row of chartJson ){
for(var value of row.values)
{
table.push(
{
column : row.header,
value : value
});
}
}
var chartJson = [{
header: '2016',
values: [1, 5, 9]
},
{
header: '2017',
values: [2, 4, 8]
},
{
header: '2018',
values: [3, 1, 5]
}
];
let table = [];
chartJson.forEach((row, index) => {
row.values.forEach((val, j) => {
table[j] = { ...table[j],
[row.header]: val
}
});
});
console.log(table)
Iterate through every chartJson's element with its' values(through inner loop) till values' length and make an object from that.
Finally, push that object into the table array.
That's it.
Have a look at the snippet below:
var chartJson = [
{
header: '2016',
values: [1, 5, 9]
},
{
header: '2017',
values: [2, 4, 8]
},
{
header: '2018',
values: [3, 1, 5]
}
];
let table = [];
let len_of_chartJson = chartJson.length, len_of_values = chartJson[0].values.length;
for (var i = 0; i < len_of_chartJson; i++) {
let obj = {};
for (var j = 0; j < len_of_values; j++) {
obj[chartJson[j].header] = chartJson[j].values[i];
}
table.push(obj);
}
console.log(table);
let table = chartJson.reduce((tbl, rec) => {
rec.values.forEach((num, index) => {
if(!tbl[index]){
tbl[index] = {}
}
tbl[index][rec.header] = num
})
return tbl
}, [])
Array reduce function is used to loop through each object, than for each object it loop through each value, checking if the index exist in the table, if it does not exist, it create an empty object at current index. Finally it creates a key value in the current index object.
You read more about reduce function below
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

How I can check if a object array key exists in Array Javascript

I have following array
let newArray = [
{
"conterTop": {
"uncategroized": [item1, item2, item3],
"categroized": [item4, item5]
}
},
{
"flooring": {
"uncategroized": [item1, item2, item3],
"categroized": [item4, item5]
}
}
]
I'm pushing countertop & flooring on runtime. Now I need to check if countertop already exists in newArray, it should not push countertop again. I have tried newArray.includes() but it's not working. Please any suggestion?
You can filter the array to look for your matching key and check the length of the filtered array. If the length is greater than 0, it exists. If not, it doesn't.
newArray.filter(x => return typeof(x.countertop) !== "undefined").length > 0
Here I just filtered out every value that doesn't have counterTop and check if any value left
let newArray = [{
"conterTop": {
"uncategroized": [1, 2, 3],
"categroized": [4, 5]
}
},
{
"flooring": {
"uncategroized": [1, 2, 3],
"categroized": [4, 5]
}
}
];
let isCounterTopExists = newArray.filter(x => x.conterTop).length > 0
console.log(isCounterTopExists);
This is what Array.prototype.some is for:
let newArray = [
{
"conterTop": {}
},
{
"flooring": {}
}
];
let testArray = [
{
"flooring": {}
}
];
const testArrayResult = testArray.some(obj => obj.conterTop);
const newArrayResult = newArray.some(obj => obj.conterTop);
console.log("testArray has 'conterTop':", testArrayResult);
console.log("newArray has 'conterTop':", newArrayResult);

flatten an array without using .flat();

how can i flatten an array without using flat(). by 1 level?
so far i have this
function flatten(array) {
let flattened = [];
for (let i = 0; i < array.length; i++) {
const current = array[i];
for (let j = 0; i < current.length; j++) {
flattened.push(current[j])
}
}
return flattened
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
flatten([[1], [2], 3, 4, [5]]);
// -> [1, 2, 3, 4, 5]
flatten([false, [true, [false]], [true]]);
// -> [false, true, [false], true]
flatten([]);
// -> []
and its crashing my memory
I hope this helps
var twoDimension = [[1], [2], 3, 4, [5]];
var plano = twoDimension.reduce((acc, el) => acc.concat(el), []);
console.log(plano);
You could use Array.reduce and the spread syntax:
function flatten(array) {
return array.reduce(
(accumulator, item) => {
// if `item` is an `array`,
// use the `spread syntax` to
// append items of the array into
// the `accumulator` array
if (Array.isArray(item)) {
return [...accumulator, ...item];
}
// otherwise, return items in the
// accumulator plus the new item
return [...accumulator, item];
}
, []); // initial value of `accumulator`
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
References:
Array.reduce - MDN
Spread syntax - MDN
To flatten by a single level only, Array#concat() can be leveraged. It accepts any amount of arguments, so an array can be spread into the function call:
[].concat(...arr)
This avoids any explicit loops. JavaScript handles everything:
function flatten(arr) {
return [].concat(...arr);
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
You can use the following method if your array have primitive data type and want to flat it completely:
arr.toString().split(',');
you can use the reducer of javascript as an alternative to flat().
const arr = [1, 2, [3, 4]];
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]
or you can use decomposition syntax
const flattened = arr => [].concat(...arr);
For more details, go to Mozilla MDN
You have an error here:
for (let j = 0; i < current.length; j++) {
// ^ wrong variable, should be j
And you need to check if the value is not an array, then just push the current value and continue the loop.
function flatten(array) {
let flattened = [];
for (let i = 0; i < array.length; i++) {
const current = array[i];
if (!Array.isArray(current)) {
flattened.push(current);
continue;
}
for (let j = 0; j < current.length; j++) {
flattened.push(current[j])
}
}
return flattened
}
console.log(flatten([['foo', 'bar'], ['baz', 'qux']]));
// -> ["foo", "bar", "baz", "qux"]
console.log(flatten([[1], [2], 3, 4, [5]]));
// -> [1, 2, 3, 4, 5]
console.log(flatten([false, [true, [false]], [true]]));
// -> [false, true, [false], true]
console.log(flatten([]));
// -> []
You had a typo where in your innermost loop you set i to 0 instead of j. The only other thing you needed to do was check to see if each element in the outer array was scalar (not an array) and push it to the returned array if so.
function flatten(arr) {
let flat = []
for (let i=0; i < arr.length; i++) {
const cur = arr[i]
if(!Array.isArray(cur)){
flat.push(cur)
}else{
for (let j=0; j < cur.length; j++) {
flat.push(cur[j])
}
}
}
return flat
}
console.log(flatten([['foo','bar'],['baz','qux']]))
console.log(flatten([[1],[2],3,4,[5]]))
console.log(flatten([false,[true,[false]],[true]]))
console.log(flatten([]))
Well you can use spread operator with reduce.
function flatten(array) {
return array.reduce((a,v) => [...a, ...(Array.isArray(v) ? v : [v])], []);
}
console.log(flatten([['foo', 'bar'], 'baz', 'qux']))
Following could be used as a general implementation of Array.prototype.flat()
function flattenArray(arr, depth = 1) {
if (!Array.isArray(arr)) {
return [arr];
}
return depth > 0
? arr.reduce(
(acc, curr) =>
acc.concat(
Array.isArray(curr) ? flattenArray(curr, depth - 1) : curr
),
[]
)
: arr.slice();
}
const a = [1, 2, 3, 4];
const b = "a";
const c = [1, [2, 3], 4];
const d = [1, [2, [3, 4], 5], 6];
const e = [1, [2, [3, [4, [5], [6]], 7], 8], 9];
console.log(flattenArray(a, Infinity));
console.log(flattenArray(b, Infinity));
console.log(flattenArray(c, Infinity));
console.log(flattenArray(d, Infinity));
console.log(flattenArray(e, Infinity));
There is another interesting way to do it.
Stringify the array
remove all array start symbol ([) and array end symbol(])
Add array start symbol at the beginning and array end symbol at the end.
Now parse the resulting string
const arr2 = [0, 1, 2, [5, [10, [3, 4]]]]
const arr2 = [0, 1, 2, [5, [10, [3, 4]]]]
console.log( JSON.parse('['+ JSON.stringify(arr2).replace(/\[/g, ' ').replace(/\]/g, ' ') + ']'))
Suppose given flatten number list without using the flat function is:
let array = [2,3,[5,2,[6,[3, [4, 5, [5, 1, 3]]]],1,1],9];
//let array= [2,3,[5,2,[6,[3, [4, 5, [5, {"key":"value"}, 3]]]],1,1],9];
//achieve above commented nested array condition using second approach.
The best answer already given by #Mahipal that would be first approach i.e.
array.toString().split(',')
with number array conversion
array.toString().split(',').map(n => +n)
another approach would be using function recursion without toString()
function flatter(arr) {
if (!Array.isArray(arr) && (!isNaN(arr) || typeof arr === "object")) {
return arr;
}
return arr.reduce((a, b) => {
a.push(...[].concat(flatter(b)));
return a;
}, [])
}
flatter(array);
and output is:
[ 2, 3, 5, 2, 6, 3, 4, 5, 5, 1, 3, 1, 1, 9 ]
Hope this would help many ones.
//Using Recursion
const arr = [1, 2, 3, 4, [5, 6, [6, 7], 7, 8]]
let arr2 = [];
function flat(arr) {
arr.forEach(element => {
if (typeof (element) == 'object') {
flat(element);
} else {
arr2.push(element);
}
});
}
flat(arr);
console.log(arr2);
var multiDimensionArray = [["a"],["b","c"],["d"]]; //array of arrays
var flatArray = Array.prototype.concat.apply([], multiDimensionArray); //flatten array of arrays
console.log(flatArray);
This worked for me:
function myFlattern(arr) {
let check;
do{
check=false;
for(let i=0;i<arr.length;i++)
{
if(Array.isArray(arr[i]))
{
check=true;
let x=arr[i];
arr.splice(i,1,...x);
}
}
}while(check)
return arr;
}
A possible alternative would be without using flat():
var arr = [['object1', 'object2'],['object1'],['object1','object2','object3']];
var flattened = [].concat.apply([],arr);
You can use this to forget about the depth of nesting:
let multiArr = [1, [1, 2, [3, 4]], [2, 4, [45, 98]]];
while (multiArr.find((elem) => Array.isArray(elem))) {
multiArr = [].concat.apply([], multiArr);
}
console.log(multiArr);

how to sort an array of numbers in javascript making sure that the first count finished before adding duplicates?

I have this array of numbers that i am working with that currently look this;
count = [1,4,3,1,2,3,4,5,6,2,3,5,7];
How can i transform and sort it to make it look like this;
count = [1,2,3,4,5,6,7,1,2,3,3,4,5];
Please help, any idea out there on how to approach this?
1) Get unique elements and sort
2) Get remaining elements and sort
3) combine (1) and (2) arrays.
count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const spSort = arr => {
const uniq = [...new Set([...arr])];
const rem = [];
const temp_set = new Set([...arr]);
arr.forEach(x => {
if (temp_set.has(x)) {
temp_set.delete(x);
} else {
rem.push(x);
}
});
return [...uniq.sort(), ...rem.sort()];
};
console.log(spSort(count));
Use a Set to create unique numbers and a hash object to keep count of duplicates:
const count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const hash = count.reduce((obj, num) => {
obj[num] = obj[num] ? ++obj[num] : 1;
return obj;
}, {});
const uniq = [...new Set(count)].sort();
uniq.forEach((num, _, arr) => {
while (--hash[num]) arr.push(num);
});
console.info(uniq);

How to filter objects with different keys in array?

I have objects like this one:
{
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [4, 5, 6]
},
{
"9.01.2020": [7, 8, 9]
}
],
"id": 7
}
and I need to filter through dates object to check if there is specific date and return it's values (ex. if there is 7.01.2020). When I try fro ex user.dates, I get array with 3 different objects but when I use filteror map method, it doesn't work.
When user pick date I need to check if this date exists and add task to the existing ones and if it's new date, add date with tasks...
Any ideas? Thanks!
Actually you need to use for loop if it's not working with filter and map. I would suggest you to access date according to keys from the above arrays
let x = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [4, 5, 6]
},
{
"9.01.2020": [7, 8, 9]
}
],
"id": 7
}
let y = Object.values(x.dates);
for(const [key,value] of Object.entries(y))
{
//getting particular date string
result = Object.keys(value);
console.log(result);
//converted date for result
date = new Date(result);
console.log(date);
}
please change include your required logic for return date.
I'd probably use a nested loop, where the inner loop is over the properties of the object:
let result = null;
outer: for (const entry of obj.dates) {
for (const [key, value] of Object.entries(entry)) {
if (key === target) {
result = value;
break outer;
}
}
}
Live Example:
const obj = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [4, 5, 6]
},
{
"9.01.2020": [7, 8, 9]
}
],
"id": 7
};
const target = "7.01.2020";
let result = null;
outer: for (const entry of obj.dates) {
for (const [key, value] of Object.entries(entry)) {
if (key === target) {
result = value;
break outer;
}
}
}
console.log(result);
That makes at most one pass through the data, stopping as soon as it finds a match.
It can be a non-nested loop if you know that the objects in obj.dates only have a single property:
let result = null;
for (const entry of obj.dates) {
const [key] = Object.keys(entry);
if (key === target) {
result = value;
break;
}
}
Live Example:
const obj = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [4, 5, 6]
},
{
"9.01.2020": [7, 8, 9]
}
],
"id": 7
};
const target = "7.01.2020";
let result = null;
for (const entry of obj.dates) {
const [key] = Object.keys(entry);
if (key === target) {
result = entry[key];
break;
}
}
console.log(result);
Another option would be to turn obj.dates into a Map and then use get on it:
const map = new Map(obj.dates
.map(entry => {
const [key] = Object.keys(entry);
return [key, entry[key]];
})
);
const result = map.get(target);
Live Example:
const obj = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [4, 5, 6]
},
{
"9.01.2020": [7, 8, 9]
}
],
"id": 7
};
const target = "7.01.2020";
const map = new Map(obj.dates
.map(entry => {
const [key] = Object.keys(entry);
return [key, entry[key]];
})
);
const result = map.get(target);
console.log(result);
That makes a full pass through the data, then a hashed (or similar) lookup on the resulting Map. It's useful if you need to look for multiple targets (since you just build the Map once). Note that this version assumes the objects in obj.dates only have one property.

Categories