@vizabi/core
Advanced tools
Comparing version 1.23.4 to 1.24.0
@@ -1,2 +0,2 @@ | ||
// http://vizabi.org v1.23.4 Copyright 2022 Jasper Heeffer and others at Gapminder Foundation | ||
// http://vizabi.org v1.24.0 Copyright 2022 Jasper Heeffer and others at Gapminder Foundation | ||
(function (global, factory) { | ||
@@ -147,10 +147,10 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('mobx')) : | ||
const comparisonToString = { | ||
"$eq": (field, val) => `row.${field} === ${val}`, | ||
"$ne": (field, val) => `row.${field} !== ${val}`, | ||
"$gt": (field, val) => `row.${field} > ${val}`, | ||
"$gte": (field, val) => `row.${field} >= ${val}`, | ||
"$lt": (field, val) => `row.${field} < ${val}`, | ||
"$lte": (field, val) => `row.${field} <= ${val}`, | ||
"$in": (field, val) => `${val}.includes(row.${field})`, | ||
"$nin": (field, val) => `!${val}.includes(row.${field})`, | ||
"$eq": (field, val) => `row["${field}"] === ${val}`, | ||
"$ne": (field, val) => `row["${field}"] !== ${val}`, | ||
"$gt": (field, val) => `row["${field}"] > ${val}`, | ||
"$gte": (field, val) => `row["${field}"] >= ${val}`, | ||
"$lt": (field, val) => `row["${field}"] < ${val}`, | ||
"$lte": (field, val) => `row["${field}"] <= ${val}`, | ||
"$in": (field, val) => `${val}.includes(row["${field}"])`, | ||
"$nin": (field, val) => `!${val}.includes(row["${field}"])`, | ||
}; | ||
@@ -464,2 +464,13 @@ | ||
} | ||
function pickMultiGroup(object, keys) { | ||
let result = [{}]; | ||
for (const key of keys) { | ||
if (key in object) { | ||
result = [].concat(object[key]).flatMap(objKey => | ||
result.map(res => Object.assign({}, res,{[key]: objKey})) | ||
); | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -1230,2 +1241,49 @@ function unique$1(...arrays) { | ||
function DataFrameMultiGroup(data, key, descKeys = []) { | ||
if (!Array.isArray(descKeys)) descKeys = [[descKeys]]; // desc keys is single string (e.g. 'year') | ||
if (!Array.isArray(descKeys[0])) descKeys = [descKeys]; // desc keys is one key (e.g. ['year']) | ||
if (!Array.isArray(key)) key = [key]; // key is single string (e.g. 'year') | ||
if (descKeys.length === 0 && data.key) descKeys = [data.key]; // descKeys is empty | ||
const group = createGroup(key, descKeys); | ||
group.setRow = (row, key) => { | ||
return getDataFrameMultiGroup(group, row).map(df => df.set(row, key)); | ||
}; | ||
group.batchSetRow = (data) => { | ||
const descKeys = group.descendantKeys; | ||
if (data.key?.length > 0 && arrayEquals(data.key, descKeys[descKeys.length - 1])) { | ||
for (let row of data.values()) { | ||
getDataFrameMultiGroup(group, row).forEach(df => df.setByStr(row[Symbol.for('key')], row)); | ||
} | ||
} else { | ||
for (let row of data.values()) { | ||
getDataFrameMultiGroup(group, row).forEach(df => df.set(row)); | ||
} | ||
} | ||
return group; | ||
}; | ||
return group.batchSetRow(data); | ||
} | ||
function getDataFrameMultiGroup(group, row) { | ||
if (group.type == 'DataFrame') return [group]; | ||
let members; | ||
if (!row) { | ||
members = [group.values().next().value]; | ||
} else { | ||
members = pickMultiGroup(row, group.key).map(keyObj => { | ||
const keyStr = group.keyFn(keyObj); | ||
if (group.has(keyStr)) { | ||
return group.get(keyStr); | ||
} else { | ||
return group.createMember(keyObj); | ||
} | ||
}); | ||
} | ||
return members.flatMap(member => getDataFrameMultiGroup(member, row)); | ||
} | ||
/** | ||
@@ -1418,2 +1476,8 @@ * | ||
function groupByWithMultiGroupMembership(df, groupKey, memberKey = df.key) { | ||
return DataFrameMultiGroup(df, groupKey, memberKey); | ||
} | ||
function fillNull(df, fillValues) { | ||
@@ -1529,2 +1593,3 @@ let concept, row; | ||
groupBy: (groupKey, memberKey) => groupBy(df, groupKey, memberKey), | ||
groupByWithMultiGroupMembership: (groupKey, memberKey) => groupByWithMultiGroupMembership(df, groupKey, memberKey), | ||
interpolate: () => interpolate(df), | ||
@@ -1531,0 +1596,0 @@ interpolateTowards: (df2, mu, fields, interpolators) => interpolateBetween(df, df2, mu, fields, interpolators), |
{ | ||
"name": "@vizabi/core", | ||
"version": "1.23.4", | ||
"version": "1.24.0", | ||
"description": "Vizabi core (data layer)", | ||
@@ -5,0 +5,0 @@ "main": "dist/Vizabi.js", |
@@ -222,4 +222,25 @@ import { resolveRef } from "../config"; | ||
}, | ||
get conceptIsEntitySetAndItsDomainIsInSpace() { | ||
//example: space ["geo"], concept: "world_4region" | ||
return this.concept && this.space && this.conceptProps.concept_type === "entity_set" && this.space.includes(this.conceptProps.domain); | ||
}, | ||
responseAdaptorHack_MoveItToDSource(response){ | ||
//this adapter handles situations such as getting world_4region property | ||
//of geos that are themselves world_4regions | ||
// Jasper: you could have that as an extra layer in datasource even or smth | ||
// for any entity query to a domain, add all is--<sets> properties for that domain to the query, | ||
// and when result comes back add <set> properties for entities that have is--<set> TRUE | ||
// or do that just when you see the key is <domain> and one of the queried properties is a set in the domain. | ||
// more specific. so only add it if it's asked for :) | ||
if (this.conceptIsEntitySetAndItsDomainIsInSpace) { | ||
response.raw.forEach(m => { | ||
m[this.concept] = m[this.concept] ?? m[this.conceptProps.domain]; | ||
}); | ||
} | ||
return response; | ||
}, | ||
fetchResponse() { | ||
const promise = this.source.query(this.ddfQuery) | ||
.then(this.responseAdaptorHack_MoveItToDSource.bind(this)) | ||
.then(response => response.forKey(this.commonSpace)); | ||
@@ -226,0 +247,0 @@ return fromPromise(promise); |
import { createStore } from '../genericStore' | ||
import { dataConfig } from './dataConfig' | ||
import { entityPropertyDataConfig } from './entityPropertyDataConfig' | ||
import { entityMembershipDataConfig } from './entityMembershipDataConfig' | ||
export const dataConfigStore = createStore(dataConfig, { | ||
entityPropertyDataConfig, | ||
entityMembershipDataConfig | ||
}); |
@@ -9,3 +9,2 @@ import { createStore } from '../genericStore' | ||
import { aggregate } from './aggregate' | ||
import { facet } from './facet' | ||
@@ -18,4 +17,3 @@ export const encodingStore = createStore(encoding, { | ||
repeat, | ||
aggregate, | ||
facet | ||
aggregate | ||
}); |
@@ -14,2 +14,20 @@ import { action, isObservableArray, observable, toJS, trace } from 'mobx'; | ||
function cleanEmptyObjectsAndArrays(obj){ | ||
function notEmpty(arg) { | ||
if (arg instanceof Date) return true; | ||
return !(arg == null || typeof arg === "object" && Object.keys(arg).length === 0 || Array.isArray(arg) && arg.length === 0); | ||
} | ||
if (Array.isArray(obj)){ | ||
obj = obj.map( d => cleanEmptyObjectsAndArrays(d) ).filter(notEmpty); | ||
} else if (typeof obj === "object"){ | ||
for (const objKey in obj) { | ||
obj[objKey] = cleanEmptyObjectsAndArrays(obj[objKey]); | ||
if (!notEmpty(obj[objKey])) delete obj[objKey]; | ||
} | ||
} | ||
return obj; | ||
} | ||
filter.nonObservable = function (config, parent, id) { | ||
@@ -68,9 +86,22 @@ | ||
}), | ||
delete: action("deleteFilter", function(marker) { | ||
if (Array.isArray(marker)) { | ||
for (const el of marker) this.delete(el) | ||
delete: action("deleteFilter", function(markerItem) { | ||
this.deleteFromMarkers(markerItem); | ||
this.deleteFromDimensionsAllINstatements(markerItem); | ||
}), | ||
clear: action("clearFilter", function() { | ||
this.config.markers = []; | ||
}), | ||
toggle: action("toggleFilter", function(marker) { | ||
if (this.has(marker)) | ||
return this.delete(marker); | ||
else | ||
return this.set(marker); | ||
}), | ||
deleteFromMarkers: action("deleteInMarkers", function(markerItem) { | ||
if (Array.isArray(markerItem)) { | ||
for (const el of markerItem) this.deleteFromMarkers(el) | ||
return; | ||
} | ||
const cfg = this.config.markers; | ||
const key = this.getKey(marker); | ||
const key = this.getKey(markerItem); | ||
if (Array.isArray(cfg)) { | ||
@@ -83,10 +114,64 @@ removeOnce(cfg, key); | ||
}), | ||
clear: action("clearFilter", function() { | ||
this.config.markers = []; | ||
addToDimensionsFirstINstatement: action("deleteInDimensions", function(markerItem, path) { | ||
if (Array.isArray(markerItem)) { | ||
for (const el of markerItem) this.addToDimensionsFirstINstatement(el) | ||
return; | ||
} | ||
const cfg = this.config.dimensions; | ||
const item = this.getKey(markerItem); | ||
let addedOnce = false; | ||
function findAndAddInArray(array, item){ | ||
const index = array.indexOf(item); | ||
if (index == -1 && !addedOnce) { | ||
array.push(item); | ||
addedOnce = true; | ||
} | ||
} | ||
function findAndAddInObject(obj, item, key) { | ||
if (key === "$in") | ||
findAndAddInArray(obj, item); | ||
else if (Array.isArray(obj)) | ||
obj.forEach( d => findAndAddInObject(d, item) ); | ||
else if (typeof obj === "object") | ||
for (const objKey in obj) findAndAddInObject(obj[objKey], item, objKey); | ||
} | ||
if (path) { | ||
const inArray = path.reduce((a, p)=>{ | ||
if (a[p] == null) a[p] = ["$in", "$or", "$and", "$nin"].includes(p) ? [] : {}; | ||
return a[p]; | ||
}, cfg); | ||
findAndAddInArray(inArray, item); | ||
} else { | ||
findAndAddInObject(cfg, item); | ||
} | ||
}), | ||
toggle: action("toggleFilter", function(marker) { | ||
if (this.has(marker)) | ||
return this.delete(marker); | ||
else | ||
return this.set(marker); | ||
deleteFromDimensionsAllINstatements: action("deleteInDimensions", function(markerItem) { | ||
if (Array.isArray(markerItem)) { | ||
for (const el of markerItem) this.deleteFromDimensionsAllINstatements(el) | ||
return; | ||
} | ||
const cfg = this.config.dimensions; | ||
const item = this.getKey(markerItem); | ||
//traverse object in search of an array containing markerItem | ||
function findAndRemoveInArray(array, item){ | ||
const index = array.indexOf(item); | ||
if (index !== -1) array.splice(index, 1); | ||
} | ||
function findAndRemoveInObject(obj, item, key) { | ||
if (key === "$in") | ||
findAndRemoveInArray(obj, item); | ||
else if (Array.isArray(obj)) | ||
obj.forEach( d => findAndRemoveInObject(d, item) ); | ||
else if (typeof obj === "object") | ||
for (const objKey in obj) findAndRemoveInObject(obj[objKey], item, objKey); | ||
} | ||
findAndRemoveInObject(cfg, item); | ||
cleanEmptyObjectsAndArrays(cfg); | ||
}), | ||
@@ -93,0 +178,0 @@ getKey(d) { |
@@ -23,3 +23,2 @@ import { trace, computed, observable, toJS, autorun } from 'mobx'; | ||
"aggregate.aggregate", | ||
//"facet.facet", | ||
"frame.frameMap", | ||
@@ -26,0 +25,0 @@ "frame.interpolate", |
@@ -558,6 +558,11 @@ import { fromPromise } from "mobx-utils"; | ||
if (source.isEntityConcept(conceptId)) { | ||
const setMembershipFlags = source.availability.data | ||
.map(m => m.value) | ||
.filter(f => f.includes("is--") || f == "un_state"); | ||
const entityQuery = dataConfig.createQuery({ | ||
space: [conceptId], | ||
concept: ["name", "rank"], | ||
concept: ["name", "rank", ...setMembershipFlags], | ||
locale: dataConfig.locale, | ||
filter: null, | ||
source | ||
@@ -564,0 +569,0 @@ }) |
@@ -10,3 +10,3 @@ import { order } from "./transforms/order"; | ||
import { addColumn } from "./transforms/addColumn"; | ||
import { groupBy } from "./transforms/group"; | ||
import { groupBy, groupByWithMultiGroupMembership } from "./transforms/group"; | ||
import { interpolate } from "./transforms/interpolate"; | ||
@@ -45,2 +45,3 @@ import { reindex } from "./transforms/reindex"; | ||
groupBy: (groupKey, memberKey) => groupBy(df, groupKey, memberKey), | ||
groupByWithMultiGroupMembership: (groupKey, memberKey) => groupByWithMultiGroupMembership(df, groupKey, memberKey), | ||
interpolate: () => interpolate(df), | ||
@@ -47,0 +48,0 @@ interpolateTowards: (df2, mu, fields, interpolators) => interpolateBetween(df, df2, mu, fields, interpolators), |
import { DataFrame } from "./dataFrame"; | ||
import { isDataFrame, createKeyFn, arrayEquals, pick } from "./dfutils"; | ||
import { isDataFrame, createKeyFn, arrayEquals, pick, pickMultiGroup } from "./dfutils"; | ||
import { extent, extentIndicesOfGroupKey, extentOfGroupKey, extentOfGroupKeyPerMarker } from "./info/extent"; | ||
@@ -8,2 +8,49 @@ import { extrapolateGroup } from "./transforms/extrapolate"; | ||
export function DataFrameMultiGroup(data, key, descKeys = []) { | ||
if (!Array.isArray(descKeys)) descKeys = [[descKeys]]; // desc keys is single string (e.g. 'year') | ||
if (!Array.isArray(descKeys[0])) descKeys = [descKeys]; // desc keys is one key (e.g. ['year']) | ||
if (!Array.isArray(key)) key = [key]; // key is single string (e.g. 'year') | ||
if (descKeys.length === 0 && data.key) descKeys = [data.key]; // descKeys is empty | ||
const group = createGroup(key, descKeys); | ||
group.setRow = (row, key) => { | ||
return getDataFrameMultiGroup(group, row).map(df => df.set(row, key)); | ||
} | ||
group.batchSetRow = (data) => { | ||
const descKeys = group.descendantKeys; | ||
if (data.key?.length > 0 && arrayEquals(data.key, descKeys[descKeys.length - 1])) { | ||
for (let row of data.values()) { | ||
getDataFrameMultiGroup(group, row).forEach(df => df.setByStr(row[Symbol.for('key')], row)); | ||
} | ||
} else { | ||
for (let row of data.values()) { | ||
getDataFrameMultiGroup(group, row).forEach(df => df.set(row)); | ||
} | ||
} | ||
return group; | ||
} | ||
return group.batchSetRow(data); | ||
} | ||
function getDataFrameMultiGroup(group, row) { | ||
if (group.type == 'DataFrame') return [group]; | ||
let members; | ||
if (!row) { | ||
members = [group.values().next().value]; | ||
} else { | ||
members = pickMultiGroup(row, group.key).map(keyObj => { | ||
const keyStr = group.keyFn(keyObj); | ||
if (group.has(keyStr)) { | ||
return group.get(keyStr); | ||
} else { | ||
return group.createMember(keyObj); | ||
} | ||
}) | ||
} | ||
return members.flatMap(member => getDataFrameMultiGroup(member, row)); | ||
} | ||
/** | ||
@@ -10,0 +57,0 @@ * |
@@ -120,2 +120,13 @@ export function getIter(iter) { | ||
} | ||
export function pickMultiGroup(object, keys) { | ||
let result = [{}]; | ||
for (const key of keys) { | ||
if (key in object) { | ||
result = [].concat(object[key]).flatMap(objKey => | ||
result.map(res => Object.assign({}, res,{[key]: objKey})) | ||
); | ||
} | ||
} | ||
return result; | ||
} | ||
@@ -122,0 +133,0 @@ export function unique(...arrays) { |
@@ -95,10 +95,10 @@ import { deepclone, pipe, isString } from "../../core/utils"; | ||
const comparisonToString = { | ||
"$eq": (field, val) => `row.${field} === ${val}`, | ||
"$ne": (field, val) => `row.${field} !== ${val}`, | ||
"$gt": (field, val) => `row.${field} > ${val}`, | ||
"$gte": (field, val) => `row.${field} >= ${val}`, | ||
"$lt": (field, val) => `row.${field} < ${val}`, | ||
"$lte": (field, val) => `row.${field} <= ${val}`, | ||
"$in": (field, val) => `${val}.includes(row.${field})`, | ||
"$nin": (field, val) => `!${val}.includes(row.${field})`, | ||
"$eq": (field, val) => `row["${field}"] === ${val}`, | ||
"$ne": (field, val) => `row["${field}"] !== ${val}`, | ||
"$gt": (field, val) => `row["${field}"] > ${val}`, | ||
"$gte": (field, val) => `row["${field}"] >= ${val}`, | ||
"$lt": (field, val) => `row["${field}"] < ${val}`, | ||
"$lte": (field, val) => `row["${field}"] <= ${val}`, | ||
"$in": (field, val) => `${val}.includes(row["${field}"])`, | ||
"$nin": (field, val) => `!${val}.includes(row["${field}"])`, | ||
} | ||
@@ -105,0 +105,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { DataFrameGroup } from "../dataFrameGroup"; | ||
import { DataFrameGroup, DataFrameMultiGroup } from "../dataFrameGroup"; | ||
@@ -9,1 +9,6 @@ export function groupBy(df, groupKey, memberKey = df.key) { | ||
export function groupByWithMultiGroupMembership(df, groupKey, memberKey = df.key) { | ||
return DataFrameMultiGroup(df, groupKey, memberKey); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1271653
13227