Sort array of object based on another array - javascript

So let's say I have an array:
const chunks = [
{id: 0, names: ['app']},
{id: 1, names: ['contact']},
{id: 2, names: ['bootstrap']}
];
And I want it to be sorted based on the names property, so the order is like in this array:
const original = ['bootstrap', 'app', 'contact'];
What is the most efficient way to do this?

You could use the delta of the indices of names in original.
const chunks = [{ id: 0, names: ['app'] }, { id: 1, names: ['contact'] }, { id: 2, names: ['bootstrap'] }],
original = ['bootstrap', 'app', 'contact'];
chunks.sort((a, b) => original.indexOf(a.names[0]) - original.indexOf(b.names[0]));
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Try this method:
const chunks = [{id: 0, names: ['app']}, {id: 1, names: ['contact']}, {id: 2, names: ['bootstrap']}];
const original = ['bootstrap', 'app', 'contact'];
let result = [];
for(let i = 0; i < original.length; i++) {
for(let j = 0; j < chunks.length; j++) {
if(chunks[j].names.indexOf(original[i]) !== -1) {
result.push(chunks[j]);
}
}
}
console.log(result);

Easy way: convert chunks into an object so you get the correct one with just the key, then map over the (already sorted) array to plug in the object in place.
cMap = chunks.reduce((p,c) => Object.assign( p, {[c.names[0]]: c} ), {});
const sorted = original.map(k => cMap[k]);

Related

How to filter an Array with another Array

I have an Array of Objects:
const array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }]
I have a second array containing the ID's that I want to filter out of the first Array:
const ids = [1, 2]
How do I create a new Array of Objects without the ID's found in ids.
This is a fairly simple filter operation
const array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
const ids = [1, 2];
var result = array.filter( x => !ids.includes(x.id));
console.log(result);
If you need to mutate the original array you can do like this:
const array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
const ids = [1, 2];
ids.forEach(idToDelete => {
const index = array.findIndex(({ id }) => id === idToDelete);
array.splice(index, 1);
});
console.log(array);
If you need a new array you can do like this:
const array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
const ids = [1, 2];
const result = array.filter(({ id }) => !ids.includes(id));
console.log(result);
You could also reassign a new array to the array variable:
let array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
const ids = [1, 2];
array = array.filter(({ id }) => !ids.includes(id));
console.log(array);
Use Array.filter :
let array = [
{id: 1, bar: "test" },
{id: 2, bar: "test2" },
{id: 3, bar: "test3" }
];
let ids = [1,2];
let filteredArray = array.filter(row=>!ids.includes(row.id));
console.log(filteredArray);
Use this oneliner from lodash.
const _ = require("lodash");
let filteredArray = _.remove(array, el=>[1,2].includes(el.id))
Use filter and indexOf.
const arr = [{ id: 1, bar: 'test' }, { id: 2, bar: 'test2' }, { id: 3, bar: 'test3' }];
const ids = [1, 2];
const result = arr.filter(element => ids.indexOf(element.id) === -1);
console.log(result);
We can filter an array in JavaScript using Array filter()
const myArray = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }]
const ids = [1,2]
const resultArray = myArray.filter(item => !ids.includes(item.id));
console.log(resultArray);
In term of performance the best solution will be the next one:
let array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
const ids = [1,2];
const idSet = new Set();
for (const id of ids) {
idSet.add(id);
}
array = array.filter(x => !set.has(x.id));
//const newArray if you need the initial array unmodified
In this case we perform two consequencial iteration instead of a nested one, so the time complexity will be O(n) instead of O(n^2);
##Edit
If you instead need the initial array to be mutated and not overwritten you can use this approach:
const ids = [1,2];
const array = [{id: 1, bar: "test" }, {id: 2, bar: "test2" }, {id: 3, bar: "test3" }];
for (const id of ids) {
const index = array.findIndex(x => x.id == id);
array.splice(index, 1);
}
In the second case the time complexity will be O(n*m), where n is array length and m is ids length.
I want to propose something wildly different.
In my case, I wanted to filter one list of unique IDs against another.
I was curious if regex could do it faster.
Such a method really only works with one-dimensional arrays of simple objects.
It's probably best if items a single regex 'word' (string of 0-9a-z_).
A list of ids works perfect.
array.filter works best on small datasets (1,000), usually slightly faster
regex worked 66% faster on large datasets (10,000)
regex speed advantage widens. 90% faster on 100,000.
On comparing two arrays of 1m items, filter didn't do anything for me after more than 90 seconds. Regex returned a result in six seconds.
In this case, the input is number[], and the output is string[], which works for my purposes, but you can use map to convert back to numbers if you need, .
var listlength = 10000;
function createArray() {
let arr = new Set();
for (let i = 0; i < listlength; i++) {
arr.add(Math.floor(Math.random() * listlength));
}
return arr;
}
function filter() {
let arr1 = Array.from(createArray());
let arr2 = Array.from(createArray());
let start = +new Date();
let arr3 = arr1.filter((n) => !arr2.includes(n));
console.log('filter', (+new Date() - start) + 'ms', arr1.length, arr2.length, arr3.length);
}
function regex() {
let arr1 = Array.from(createArray());
let arr2 = Array.from(createArray());
let start = +new Date();
let str1 = arr1.join(',') + ',';
str1 = str1.replace(new RegExp('\\b(' + arr2.join('|') + '),', 'g'), '');
let result = str1.split(',') // .map(e=>Number(e)); (to convert back to number[])
result.pop();
console.log('regex', (+new Date() - start) + 'ms', arr1.length, arr2.length, result.length);
}
for (let x = 0; x < 10; x++) {
console.log(`try ${x}`);
filter();
regex();
}
On my NodeJS app, sets of 100,000, regex more than 90% faster.

Create array of objects from an object using javascipt?

My initial data is coming like this..
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
I need output in this format..
FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
];
Separated the keys and values from the initial object. But struck in creating array of objects from the same. Please help.
You could get the entries and map the inner array with the values at the given index.
let initial = { labels: ['label1', 'label2', 'label3'], children: ['child1', 'child2', 'child3'], numbers: [1, 2, 3] },
result = Object
.entries(initial)
.reduce((r, [k, a]) => a.map((v, i) => ({ ...(r[i] || {}), [k]: v })), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This should do exactly what you want, assuming all arrays have equal length. With a bit of creativity this algorithm can be generalized to objects of arbitrary fields count. Here's the fiddle.
let initial = {
labels: [
'label1',
'label2',
'label3'
],
children: [
'child1',
'child2',
'child3'
],
numbers: [
1,
2,
3
]
};
function convertObjectToArray(
labels,
children,
numbers
) {
let final = [];
for (let i = 0; i < numbers.length; i++) {
final.push({
labels: labels[i],
children: children[i],
numbers: numbers[i],
});
}
return final;
}
console.log(convertObjectToArray(
initial.labels,
initial.children,
initial.numbers
));
I don't know of any built in javascript functions, but you can create a for loop (assuming that all arrays in the object are of same length):
for(let i = 0; i < initial[Object.keys(initial)[0]].length; i++){
FINAL_OUTPUT.push({
labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
});
}
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
let keys = Object.keys(initial);
let keyLength = keys[0].length;
let sampleValueArray = initial[keys[0]];
let i = 0;
let result = sampleValueArray.map( (item,index) => {
let temp = {};
keys.forEach( key =>
temp[key] = initial[key][index]
)
return temp
})
console.log(result)
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
}
/* FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
]; */
const result = Object.keys(initial).reduce((acc, x, i, keys) => {
const arr = keys.map((y, j) => initial[y][i]);
acc = [...acc, keys.reduce((acc_2, z, k) => ({
...acc_2,
[z]: arr[k]
}), [])]
return acc
}, [])
console.log(result)
let FINAL_OUTPUT =[]
for(let i =0;i<initial.length;i++){
FINAL_OUTPUT.push(
{labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
})
}
You can use lodash's _.flow() to create a function that:
Uses to pairs to get an array of array of [key, [values]] pairs
Unzip to get separate keys from values
Destructure the keys and the values. Use _.unzip() to transpose the values, and then map, and use _.zipObject() with the keys to create the objects.
const { flow, toPairs, unzip, zipObject } = _;
const fn = flow(
toPairs, // convert to [key, values] pair
unzip, // transpose to array of keys, and array of values
([keys, values]) => // destructure keys and values
unzip(values) // transpose the values
.map(vals => zipObject(keys, vals)) // create object from keys and each set of values
);
const initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
};
const result = fn(initial);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Use object property values of two JS array of objects

I have two arrays
let arr1 = [{'id': 'ee', 'seat': '12'},
{'id': 'aa', 'seat': '8'}
]
let arr2 = [
{'id': 's22', 'num': ''},
{'id': '2s2', 'num': ''}
]
I want to copy seat values from arr1 to num property at arr2, but I only get last arr1 seat value in for loop.
for( let i = 0; i <= arr1.length; i++) {
for( let x = 0; x <= arr2.length; x++) {
arr2[x]['num'] = arr1[i]['seat'];
}
}
console.log(arr2);
Iterate arr2 with Array.forEach(), and take the respective seat value by index from arr1:
const arr1 = [{'id': 'ee', 'seat': '12'},{'id': 'aa', 'seat': '8'}]
const arr2 = [{'id': 's22', 'num': ''},{'id': '2s2', 'num': ''}]
arr2.forEach((o, i) => o.num = arr1[i].seat)
console.log(arr2)
You need just a single loop and check if the index of the array if is (only) smaller than the length of the minimum of both arrays.
If the index get the length of an array, the access returns undefined, because this element is not in the array.
A further access to a property of this throws an error:
Unable to get property 'seat' of undefined or null reference
var arr1 = [{ id: 'ee', seat: '12' }, { id: 'aa', seat: '8' }],
arr2 = [{ id: 's22', num: '' }, { id: '2s2', num: '' }],
i, l;
for (i = 0, l = Math.min(arr1.length, arr2.length); i < l; i++) {
arr2[i].num = arr1[i].seat;
}
console.log(arr2);
You can do it in just one for loop.
for(let i = 0; i < arr1.length; i++) {
arr2[i].num = arr1[i].seat;
}
Hope this helps!
Assuming you want to match indices, this should do it.
const arr1 = [
{'id': 'ee', 'seat': '12'},
{'id': 'aa', 'seat': '8'}
]
const arr2 = [
{'id': 's22', 'num': ''},
{'id': '2s2', 'num': ''}
]
const result = arr2.map((e, i) => ({...e, ...{num: arr1[i].seat}}))
console.log(result)
If you want all of the seats in each num, it wouldn't be much harder.

Nested duplicate arrays in objects

I am trying to take an array of objects and do 2 things.
1.) Remove objects from the array that are duplicated, create a new array with the names of the items that were duplicates.
Original:
var duplicates = [];
var objects = [
{
name: 'foo',
nums: [1,2]
},
{
name: 'bar',
nums: [3,2]
},
{
name: 'baz',
nums: [1,2]
},
{
name: 'bum',
nums: [2,3]
},
{
name: 'bam',
nums: [1,2]
},
]
Desired Output:
duplicates = ['foo', 'baz', 'bam'];
objects = [
{
name: 'bar',
nums: [3,2]
},
{
name: 'bum',
nums: [2,3]
}
]
Can anyone help with this? I am using lodash in my project.
If order of elements in nums array matters:
_.pluck(_.flatten(_.filter(_.groupBy(objects, "nums"), function(el) {
return (el.length !== 1)
})), "name")
or a bit tidier
var hmap = _(objects).groupBy("nums").values();
var unique = hmap.where({'length': 1}).flatten().value();
var duplicates = hmap.flatten().difference(unique).value();
I don't know underscore.js, here's how to do it with plain JS:
var counts = {};
var duplicates = [];
for (var i = 0; i < objects.length; i++) {
var str = JSON.stringify(objects[i].nums);
if (str in counts) {
counts[str]++;
} else {
counts[str] = 1;
}
}
objects = objects.filter(function(val) {
if (counts[JSON.stringify(val.nums)] == 1) {
return true;
} else {
duplicates.push(val.name);
return false;
}
});
DEMO

Concatenate two JSON objects

I have two JSON objects with the same structure and I want to concat them together using Javascript. Is there an easy way to do this?
Based on your description in the comments, you'd simply do an array concat:
var jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23}];
var jsonArray2 = [{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
jsonArray1 = jsonArray1.concat(jsonArray2);
// jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23},
//{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
If you'd rather copy the properties:
var json1 = { value1: '1', value2: '2' };
var json2 = { value2: '4', value3: '3' };
function jsonConcat(o1, o2) {
for (var key in o2) {
o1[key] = o2[key];
}
return o1;
}
var output = {};
output = jsonConcat(output, json1);
output = jsonConcat(output, json2);
Output of above code is{ value1: '1', value2: '4', value3: '3' }
The actual way is using JS Object.assign.
Object.assign(target, ...sources)
MDN Link
There is another object spread operator which is proposed for ES7 and can be used with Babel plugins.
Obj = {...sourceObj1, ...sourceObj2}
I use:
let x = { a: 1, b: 2, c: 3 }
let y = {c: 4, d: 5, e: 6 }
let z = Object.assign(x, y)
console.log(z)
// OUTPUTS:
{ a:1, b:2, c:4, d:5, e:6 }
From here.
You can use jquery extend method.
Example:
o1 = {"foo":"bar", "data":{"id":"1"}};
o2 = {"x":"y"};
sum = $.extend(o1, o2);
Result:
sum = {"foo":"bar", "data":{"id":"1"}, "x":"y"}
One solution is to use a list/array:
var first_json = {"name":"joe", "age":27};
var second_json = {"name":"james", "age":32};
var jsons = new Array();
jsons.push(first_json);
jsons.push(second_json);
Result
jsons = [
{"name":"joe", "age":27},
{"name":"james", "age":32}
]
if using TypeScript, you can use the spread operator (...)
var json = {...json1,...json2}
You can use Object.assign() method. The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.[1]
var o1 = { a: 1 }, o2 = { b: 2 }, o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
okay, you can do this in one line of code. you'll need json2.js for this (you probably already have.). the two json objects here are unparsed strings.
json1 = '[{"foo":"bar"},{"bar":"foo"},{"name":"craig"}]';
json2 = '[{"foo":"baz"},{"bar":"fob"},{"name":"george"}]';
concattedjson = JSON.stringify(JSON.parse(json1).concat(JSON.parse(json2)));
Just try this, using underscore
var json1 = [{ value1: '1', value2: '2' },{ value1: '3', value2: '4' }];
var json2 = [{ value3: 'a', value4: 'b' },{ value3: 'c', value4: 'd' }];
var resultArray = [];
json1.forEach(function(obj, index){
resultArray.push(_.extend(obj, json2[index]));
});
console.log("Result Array", resultArray);
Result
var baseArrayOfJsonObjects = [{},{}];
for (var i=0; i<arrayOfJsonObjectsFromAjax.length; i++) {
baseArrayOfJsonObjects.push(arrayOfJsonObjectsFromAjax[i]);
}
I use:
let jsonFile = {};
let schemaJson = {};
schemaJson["properties"] = {};
schemaJson["properties"]["key"] = "value";
jsonFile.concat(schemaJson);
The simplest way :
const json1 = { value1: '1', value2: '2' };
const json2 = { value2: '4', value3: '3' };
const combinedData = {
json1,
json2
};
console.log(combinedData)
I dont know if you want this:
U can use this for create from arrays, all arrays need contains the same number of elments.
Example:
If you have:
let a = ["a", "b", "c"];
let b = [1, 2, 3];
Use
concatArraysLikeJson([a, b]);
The result of is:
let result = {
0 : ["a", 1],
1 : ["b", 2],
2 : ["c", 3]
};
Typescript
concatArraysLikeJson(arrays:any){
let result:any = {};
let size:number = 0;
let make:boolean = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
Javascript:
concatArraysLikeJson(arrays){
let result = {};
let size = 0;
let make = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
The JSON Objects and Arrays can be combined in multiple ways within a structure
I can merge json with rules using json-object-merge
import JSONObjectMerge from "json-object-merge";
const target = {
store: {
book: [
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
};
const source = {
store: {
book: [
{
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
}
]
}
};
const merged = JSONObjectMerge(target, source, { "$.store.book": "PREPEND" });
expect(merged).toEqual({
store: {
book: [
{
// books from source are prepended to the original array
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
},
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
});

Categories

Resources